Java Poi-tl根據(jù)模板導(dǎo)出Word文件
Poi-tl官網(wǎng):Poi-tl Documentation
最近又碰上了導(dǎo)出,真的服了,還是低代碼的導(dǎo)出,其中過程的曲折真的是一言難盡..........平臺(tái)各種適配,依賴沖突,真的是崩潰.......
簡單說一下為什么使用Poi-tl,最開始想用的是poi實(shí)現(xiàn),但是結(jié)果就是poi不好控制圖片的位置,因?yàn)閳D片插入之后是“浮于文字上方”,單純的插入圖片會(huì)占行數(shù),為了方便控制格式后續(xù)就采用了poi-tl但是他同樣也不支持插入圖片之后將圖片設(shè)置為“浮于文字上方”,也是找了一些博客參考,最終實(shí)現(xiàn)了想要的效果
參考博客:poi-tl導(dǎo)出word實(shí)現(xiàn)圖片環(huán)繞方式為浮于在文字上方辦法
老樣子上效果圖:
實(shí)現(xiàn)步驟
1.準(zhǔn)備模板
2.添加依賴
<dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.10.0-beta</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency>
3.代碼實(shí)現(xiàn)
package com.example.demo; import cn.hutool.core.util.URLUtil; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.config.ConfigureBuilder; import com.deepoove.poi.data.PictureRenderData; import com.deepoove.poi.data.Pictures; import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; import com.example.controller.MyPictureRenderPolicy; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class StudentCardExporter001 { public static void main(String[] args) { // 創(chuàng)建數(shù)據(jù)映射列表 List<Map<String, Object>> sectionList = new ArrayList<>(); HashMap<String, Object> sectionMap = new HashMap<>(); sectionMap.put("zhenghao", "AS011"); sectionMap.put("chexing", "C2"); sectionMap.put("username", "楊無敵"); sectionMap.put("sex", "男"); sectionMap.put("birthday", "1983-12-06"); sectionMap.put("deptname", "北京XXXXXXX"); sectionMap.put("date", "2025-12-09"); sectionMap.put("localPicture", Pictures.ofLocal( "D:\\xiazai\\1212.jpg").size(45, 65).create()); sectionList.add(sectionMap); // 這里可以設(shè)置多條數(shù)據(jù) /*sectionMap.put("zhenghao", "AS011"); sectionMap.put("chexing", "C2"); sectionMap.put("username", "光頭強(qiáng)"); sectionMap.put("sex", "男"); sectionMap.put("birthday", "1983-12-06"); sectionMap.put("deptname", "北京XXXXXXX"); sectionMap.put("date", "2025-12-09"); sectionMap.put("localPicture", Pictures.ofLocal( "D:\\xiazai\\1212.jpg").size(45, 65).create()); sectionList.add(sectionMap);*/ HashMap<String, Object> paramMap = new HashMap<>(); paramMap.put("sectionName", sectionList); ArrayList<Object> titleList = new ArrayList<>(); // 創(chuàng)建最終的數(shù)據(jù)映射 for (int i = 0; i < 2; i++) { HashMap<String, Object> titleMap = new HashMap<>(); titleMap.put("oneTitle", " 1、駕駛車輛必須攜帶此證,以備檢查。"); titleMap.put("tweTitle", " 2、持證人必須遵守本公司車輛管理規(guī)定及《中華\n" + " 人民共和國道路交通安全法》。"); titleMap.put("threeTitle", " 3、遺失此證必須及時(shí)報(bào)告發(fā)證單位。"); titleMap.put("fourTitle", " 4、此證不得偽造、涂改,不得轉(zhuǎn)借他人;一經(jīng)發(fā)\n" + " 現(xiàn),立即取消其本人準(zhǔn)駕資格并收回其此證。\n"); titleList.add(titleMap); } paramMap.put("attention", titleList); // 加載模板文件 PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { org.springframework.core.io.Resource[] resources = resolver.getResources("classpath:excelTemplate/model1.docx"); if (resources.length == 0) { throw new IOException("Template file not found."); } InputStream inputStream = resources[0].getInputStream(); ConfigureBuilder builder = Configure.builder(); builder.addPlugin('%', new MyPictureRenderPolicy()); // 編譯并渲染模板 XWPFTemplate template = XWPFTemplate.compile(inputStream, builder.build()).render(paramMap); // XWPFTemplate compile = XWPFTemplate.compile(inputStream, builder.build()).render(paramMap); // 輸出生成的文檔 try (FileOutputStream out = new FileOutputStream("D:\\xiazai\\1209.docx")) { template.write(out); } } catch (IOException e) { e.printStackTrace(); } } }
自定義MyPictureRenderPolicy
package com.example.controller; import com.deepoove.poi.data.PictureRenderData; import com.deepoove.poi.data.PictureType; import com.deepoove.poi.data.Pictures; import com.deepoove.poi.data.style.PictureStyle; import com.deepoove.poi.exception.RenderException; import com.deepoove.poi.policy.AbstractRenderPolicy; import com.deepoove.poi.render.RenderContext; import com.deepoove.poi.util.BufferedImageUtils; import com.deepoove.poi.util.SVGConvertor; import com.deepoove.poi.util.UnitUtils; import com.deepoove.poi.xwpf.BodyContainer; import com.deepoove.poi.xwpf.BodyContainerFactory; import com.deepoove.poi.xwpf.WidthScalePattern; import com.deepoove.poi.xwpf.XWPFRunWrapper; import com.example.demo.ExportUtil; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.IBodyElement; import org.apache.poi.xwpf.usermodel.ParagraphAlignment; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.function.Supplier; public class MyPictureRenderPolicy extends AbstractRenderPolicy<Object> { @Override protected boolean validate(Object data) { if (null == data) { return false; } else if (data instanceof PictureRenderData) { return null != ((PictureRenderData) data).getPictureSupplier(); } else { return true; } } @Override public void doRender(RenderContext<Object> context) throws Exception { MyPictureRenderPolicy.Helper.renderPicture(context.getRun(), wrapper(context.getData())); } @Override protected void afterRender(RenderContext<Object> context) { this.clearPlaceholder(context, false); } @Override protected void reThrowException(RenderContext<Object> context, Exception e) { this.logger.info("Render picture " + context.getEleTemplate() + " error: {}", e.getMessage()); String alt = ""; if (context.getData() instanceof PictureRenderData) { alt = ((PictureRenderData) context.getData()).getAltMeta(); } context.getRun().setText(alt, 0); } private static PictureRenderData wrapper(Object object) { return object instanceof PictureRenderData ? (PictureRenderData) object : Pictures.of(object.toString()).fitSize().create(); } public static class Helper { public static void renderPicture(XWPFRun run, PictureRenderData picture) throws Exception { Supplier<byte[]> supplier = picture.getPictureSupplier(); byte[] imageBytes = (byte[]) supplier.get(); if (null == imageBytes) { throw new IllegalStateException("Can't read picture byte arrays!"); } else { PictureType pictureType = picture.getPictureType(); if (null == pictureType) { pictureType = PictureType.suggestFileType(imageBytes); } if (null == pictureType) { throw new RenderException("PictureRenderData must set picture type!"); } else { PictureStyle style = picture.getPictureStyle(); if (null == style) { style = new PictureStyle(); } int width = style.getWidth(); int height = style.getHeight(); if (pictureType == PictureType.SVG) { imageBytes = SVGConvertor.toPng(imageBytes, (float) width, (float) height); pictureType = PictureType.PNG; } if (!isSetSize(style)) { BufferedImage original = BufferedImageUtils.readBufferedImage(imageBytes); width = original.getWidth(); height = original.getHeight(); if (style.getScalePattern() == WidthScalePattern.FIT) { BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(((IBodyElement) run.getParent()).getBody()); int pageWidth = UnitUtils.twips2Pixel(bodyContainer.elementPageWidth((IBodyElement) run.getParent())); if (width > pageWidth) { double ratio = (double) pageWidth / (double) width; width = pageWidth; height = (int) ((double) height * ratio); } } } InputStream stream = new ByteArrayInputStream(imageBytes); Throwable var25 = null; try { PictureStyle.PictureAlign align = style.getAlign(); if (null != align && run.getParent() instanceof XWPFParagraph) { ((XWPFParagraph) run.getParent()).setAlignment(ParagraphAlignment.valueOf(align.ordinal() + 1)); } XWPFRunWrapper wrapper = new XWPFRunWrapper(run, false); wrapper.addPicture(stream, pictureType.type(), "Generated", Units.pixelToEMU(width), Units.pixelToEMU(height)); CTDrawing drawing = run.getCTR().getDrawingArray(0); CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic(); //拿到新插入的圖片替換添加CTAnchor 設(shè)置浮動(dòng)屬性 刪除inline屬性 CTAnchor anchor = ExportUtil.getAnchorWithGraphic(graphicalobject, "Generated", Units.toEMU(width), Units.toEMU(height),//圖片大小 Units.toEMU(180), Units.toEMU(-60), false);//相對(duì)當(dāng)前段落位置 需要計(jì)算段落已有內(nèi)容的左偏移 drawing.setAnchorArray(new CTAnchor[]{anchor});//添加浮動(dòng)屬性 drawing.removeInline(0);//刪除行內(nèi)屬性 } catch (Throwable var20) { var25 = var20; throw var20; } finally { if (stream != null) { if (var25 != null) { try { stream.close(); } catch (Throwable var19) { var25.addSuppressed(var19); } } else { stream.close(); } } } } } } } private static boolean isSetSize(PictureStyle style) { return (style.getWidth() != 0 || style.getHeight() != 0) && style.getScalePattern() == WidthScalePattern.NONE; } }
圖片處理
package com.example.demo; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; import java.math.BigInteger; public class ExportUtil { /** * @param ctGraphicalObject 圖片數(shù)據(jù) * @param deskFileName 圖片描述 * @param width 寬 * @param height 高 * @param leftOffset 水平偏移 left * @param topOffset 垂直偏移 top * @param behind 文字上方,文字下方 * @return * @throws Exception */ public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject, String deskFileName, int width, int height, int leftOffset, int topOffset, boolean behind) { String anchorXML = "<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" " + "simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">" + "<wp:simplePos x=\"0\" y=\"0\"/>" + "<wp:positionH relativeFrom=\"character\">" + "<wp:posOffset>" + leftOffset + "</wp:posOffset>" + "</wp:positionH>" + "<wp:positionV relativeFrom=\"paragraph\">" + "<wp:posOffset>" + topOffset + "</wp:posOffset>" + "</wp:positionV>" + "<wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>" + "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>" + "<wp:wrapNone/>" + "<wp:docPr id=\"1\" name=\"Drawing 0\" descr=\"" + deskFileName + "\"/><wp:cNvGraphicFramePr/>" + "</wp:anchor>"; CTDrawing drawing = null; try { drawing = CTDrawing.Factory.parse(anchorXML); } catch (XmlException e) { e.printStackTrace(); } CTAnchor anchor = drawing.getAnchorArray(0); anchor.setGraphic(ctGraphicalObject); // 減少左偏移量以使圖片更靠前 leftOffset = Math.max(0, leftOffset - 500); // 根據(jù)需要調(diào)整減去的值 anchor.getPositionH().setPosOffset(leftOffset); anchor.getPositionV().setPosOffset(topOffset); return anchor; } }
到此這篇關(guān)于Java Poi-tl根據(jù)模板導(dǎo)出Word文件的文章就介紹到這了,更多相關(guān)Java Poi-tl導(dǎo)出Word內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java IO數(shù)據(jù)操作流、對(duì)象序列化、壓縮流代碼解析
這篇文章主要介紹了java IO數(shù)據(jù)操作流、對(duì)象序列化、壓縮流代碼解析,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01springboot升級(jí)到j(luò)dk21最新教程(2023年)
你還在使用jdk8?快來看看最新出爐的SpringBoot+jdk21如何使用,下面這篇文章主要給大家介紹了關(guān)于springboot升級(jí)到j(luò)dk21的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10非常適合新手學(xué)生的Java線程池優(yōu)化升級(jí)版
作者是一個(gè)來自河源的大三在校生,以下筆記都是作者自學(xué)之路的一些淺薄經(jīng)驗(yàn),如有錯(cuò)誤請(qǐng)指正,將來會(huì)不斷的完善筆記,幫助更多的Java愛好者入門2022-03-03Java中Boolean與字符串或者數(shù)字1和0的轉(zhuǎn)換實(shí)例
下面小編就為大家?guī)硪黄狫ava中Boolean與字符串或者數(shù)字1和0的轉(zhuǎn)換實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07