高效办公自动化:POI-TL在Word模板引擎中的高级应用技巧


<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.9.1</version></dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.2</version></dependency>



publicstaticvoid main(String[] args) {//获取文件模版 InputStream inputStream = ClassLoader.getSystemResourceAsStream("word/快速开始示例.docx");if (inputStream == null) {thrownew IllegalArgumentException("模板文件未找到:word/快速开始示例.docx"); }//模板数据 Map<String, Object> data = new HashMap<>(); data.put("title", "hello world");//模板引擎初始化,加载模板文件,开始渲染数据(word模板中默认以{{}}双层花括号符号填充占位符,//可以在配置中修改为自己喜欢的符号类型,如{},在配置demo中会详细介绍)try(XWPFTemplate template = XWPFTemplate.compile(inputStream).render(data)) { template.write(new FileOutputStream("生成结果-快速开始示例.docx")); } catch (Exception e) { e.printStackTrace(); } }


//普通文本填充data.put("title", "hello world");//带格式文本填充data.put("colorTitle", Texts.of("hello world").color("FF00FF").create());//链接文本填充data.put("link", Texts.of("baidu").link("https://www.baidu.com/").create());


//读取图片文件try { InputStream picInStream = ClassLoader.getSystemResourceAsStream("word/photo.jpg");if (picInStream == null) {thrownew IllegalArgumentException("模板文件未找到:word/photo.jpg"); } data.put("image", Pictures.ofStream(picInStream, PictureType.JPEG).size(200, 220).create()); } catch (Exception e) { e.printStackTrace(); }

//表格填充String[][] tables = {newString[] { "姓名", "年龄"},newString[] { "张三", "23"},newString[] { "李四", "24"}}; data.put("table0", Tables.of(tables).border(TableStyle.BorderStyle.DEFAULT).create());//设置格式填充表格 RowRenderData row0 = Rows.of("类型", "颜色").textColor("FF00FF").bgColor("4472C4").center().create(); RowRenderData row1 = Rows.create("小狗", "白色"); RowRenderData row2 = Rows.create("小猫", "黑色"); RowRenderData row3 = Rows.create("小兔子", "灰色"); data.put("table1", Tables.create(row0, row1,row2, row3));//设置合并单元格 RowRenderData rowMerge = Rows.of("菜名", "价格", "数量").center().bgColor("4472C4").create(); RowRenderData rowMerge1 = Rows.create("没有数据", null, null); RowRenderData rowMerge2 = Rows.create("红烧肉", "35元", "2份"); RowRenderData rowMerge3 = Rows.create("牛肉粉", "28元", "3份"); MergeCellRule rule = MergeCellRule.builder().map(MergeCellRule.Grid.of(1, 0), MergeCellRule.Grid.of(1, 2)).build(); data.put("table2", Tables.of(rowMerge, rowMerge1, rowMerge2, rowMerge3).mergeRule(rule).create());


//列表默认填充data.put("list", Numberings.create("春节", "清明", "五一", "端午", "中秋", "国庆"));//有序数字列表data.put("list1",Numberings.of(NumberingFormat.DECIMAL) // 可以有多种有序编号方式 .addItem("星期一") .addItem("星期二") .addItem("星期三") .addItem("星期四") .addItem("星期五") .addItem("星期六") .addItem("星期日") .create());


//填充false区块对填充 data.put("falseBlock", false);//填充非false区块对填充 data.put("notFalseBlock", true);//填充自定义对象区块对填充 data.put("BlockMap", new Person("张三"));//填充自定义对象列表区块对填充 data.put("BlockListMap", Arrays.asList(new Person("李四"), new Person("王五"), new Person("周六")));


//引用图片填充 InputStream picInStream = ClassLoader.getSystemResourceAsStream("word/quotePhoto.jpg");if (picInStream == null) {thrownew IllegalArgumentException("模板文件未找到:word/quotePhoto.jpg"); }

//引用表格填充 ChartMultiSeriesRenderData chart = Charts//填充的为图表标题跟X轴 .ofMultiSeries("折线图", newString[] { "00:15", "00:30", "00:45", "01:00", "01:15", "01:30", "01:45", "02:00"})//Y轴数据依次在模板中填充 .addSeries("价格1", new Double[] { 370.13, 384.12, 325.12, 396.12, 427.12, 368.12, 329.12, 300.12 }) .addSeries("价格2", new Double[] { 410.13, 356.12, 432.12, 323.12, 367.12, 432.12, 321.12, 345.12 }) .addSeries("价格3", new Double[] { 368.12, 382.12, 443.12, 364.12, 325.12, 356.12, 307.12, 298.12 }) .create(); data.put("lineChart", chart);




//引用单系列图表填充 ChartSingleSeriesRenderData pie = Charts .ofSingleSeries("类型占比", newString[] { "类型1", "类型2","类型3","类型4" }) .series("比例", new Integer[] { 30, 35,25,10 }) .create(); data.put("cakeChart", pie);




//引用组合图表填充 ChartMultiSeriesRenderData comb = Charts//填充的为图表标题跟X轴 .ofComboSeries("市场", newString[] { "00:15", "00:30", "00:45", "01:00", "01:15", "01:30", "01:45", "02:00"})//填充面积图 .addAreaSeries("预测1", new Double[] { 1456.21, 1234.56, 1356.21, 1123.45, 1245.56, 1323.45, 1434.56, 1623.45 })//填充柱状图 .addBarSeries("预测2", new Double[] { 1256.21, 1324.56, 1135.62, 1212.34, 1345.56, 1223.45, 1134.56, 1223.45 })//填充折线图(折线图在模板中设置的时副Y轴) .addLineSeries("预测3", new Double[] { 123.45, 87.56, 96.67, 66.78, 112.89, 123.90, 89.01, 90.12 }).create(); data.put("combinationChart", comb);





//配置标签 ConfigureBuilder builder = Configure.builder();//自定义标签为{} builder.buildGramer("{", "}");//模板数据Map<String, Object> data = new HashMap<>(); data.put("title", "hello world");


//配置标签 ConfigureBuilder builder = Configure.builder();//自定义标签为{} builder.buildGramer("{", "}");//自定义图片标签 builder.addPlugin('%', new PictureRenderPolicy());//模板数据Map<String, Object> data = new HashMap<>(); data.put("title", "hello world");//读取图片文件try { InputStream picInStream = ClassLoader.getSystemResourceAsStream("word/photo.jpg");if (picInStream == null) {thrownew IllegalArgumentException("模板文件未找到:word/photo.jpg"); } data.put("image", Pictures.ofStream(picInStream, PictureType.JPEG).size(200, 220).create()); } catch (Exception e) { e.printStackTrace(); }

publicvoid render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) { XWPFRun run = ((RunTemplate) eleTemplate).getRun();//"Hello, world" + 标签的内容String thing = "Hello, world".concat(String.valueOf(data)); run.setText(thing, 0); }
//配置 ConfigureBuilder builder = Configure.builder();//title标签 采用HelloWorldRenderPolicy渲染策略 builder.bind("title", new HelloWorldRenderPolicy());Map<String, Object> data = new HashMap<>(); data.put("title", "你好世界");


//行填充策略 HackLoopTableRenderPolicy rowRenderPolicy = new HackLoopTableRenderPolicy(); List<Information> informationList = new ArrayList<>(); informationList.add(new Information("张三", "18", "男", "医生", "跑步")); informationList.add(new Information("李四", "21", "女", "科学家", "骑车")); informationList.add(new Information("王五", "25", "男", "教师", "游泳"));//informationList标签 采用rowRenderPolicy渲染策略 builder.bind("informationList", rowRenderPolicy); data.put("informationList", informationList);


// 列填充策略 LoopColumnTableRenderPolicy columnRenderPolicy = new LoopColumnTableRenderPolicy(); List<Information> informationColumnList = new ArrayList<>(); informationColumnList.add(new Information("张三", "18", "男", "医生", "跑步")); informationColumnList.add(new Information("李四", "21", "女", "科学家", "骑车")); informationColumnList.add(new Information("王五", "25", "男", "教师", "游泳"));//informationColumnList标签 采用columnRenderPolicy渲染策略 builder.bind("informationColumnList", columnRenderPolicy); data.put("informationColumnList", informationColumnList);


// 拿到数据 Map<String, List<RowRenderData>> map = (Map<String, List<RowRenderData>>) data;//自定义数据是从第二行和第四行开始int dogRow = 2;int peopleRow = 4; List<RowRenderData> dogs = map.get("dogs"); List<RowRenderData> people = map.get("peoples");//从表格下部开始填充数据,可以保证索引正确if (people!= null) { table.removeRow(peopleRow);for(int i=0;i<people.size();i++){ XWPFTableRow insertNewTableRow = table.insertNewTableRow(peopleRow);for (int j = 0; j < 5; j++) insertNewTableRow.createCell();// 合并单元格 TableTools.mergeCellsHorizonal(table, peopleRow, 0, 2);// 单行渲染 TableRenderPolicy.Helper.renderRow(table.getRow(peopleRow), people.get(i)); } }if (dogs!= null) { table.removeRow(dogRow);for(int i=0;i<dogs.size();i++){ XWPFTableRow insertNewTableRow = table.insertNewTableRow(dogRow);for (int j = 0; j < 5; j++) insertNewTableRow.createCell();// 单行渲染 TableRenderPolicy.Helper.renderRow(table.getRow(dogRow), dogs.get(i)); } }

try { InputStream inputStream = ClassLoader.getSystemResourceAsStream("word/读取解析文件示例.docx"); XWPFDocument doc = new XWPFDocument(inputStream);// 移除文档中的第3个段落 doc.getXWPFDocument().removeBodyElement(3);// 获取文档中的段落List<XWPFParagraph> paras = doc.getParagraphs();// 遍历段落for (XWPFParagraph para : paras) {// 获取段落中的文本List<XWPFRun> runs = para.getRuns();// 遍历文本for (XWPFRun run : runs) { System.out.println(run.getText(0)); } }// 获取文档中的表格List<XWPFTable> tables = doc.getTables();// 遍历表格for (XWPFTable table : tables) {// 遍历表格中的行List<XWPFTableRow> rows = table.getRows();for (XWPFTableRow row : rows) {// 遍历表格中该行所有的列List<XWPFTableCell> cells = row.getTableCells();for (XWPFTableCell cell : cells) {// 获取单元格中的段落List<XWPFParagraph> paragraphs = cell.getParagraphs();// 遍历单元格中的段落for (XWPFParagraph paragraph : paragraphs) {// 获取段落中的文本List<XWPFRun> runs = paragraph.getRuns();// 遍历文本for (XWPFRun run : runs) { System.out.println(run.getText(0)); } } } } }/*List<String> stringList = paras.stream().map(x -> x.getText()).collect(Collectors.toList()); System.out.println(stringList);*/ } catch (Exception e) { e.printStackTrace(); }


-
poi-tl程序执⾏过程出现NoSuchMethodError、ClassNotFoundException、NoClassDefFoundError异常? 解决⽅案:poi-tl依赖的apache-poi缺失或对应poi版本不兼容,尝试更换版本。 -
刚开始在使⽤poi-tl时,可能会遇到模板标签⽆法正确解析的问题。例如,标签未被替换或替换后的内容样式 丢失?解决⽅案:确保模板中的标签格式正确,例如{{tag}}。标签必须严格按照项⽬⽂档中的规范书写,并注意⼤⼩写敏感。 -
poi-tl表格的⾏循环数据加载,当表格表头存在合并单元格时,加载数据出现格式错乱?解决⽅案:需将⻚签放在⾮合并单元格内以保证格式正常。 -
poi-tl在填充段落数据的过程中,假如某⼀个标签的数据为空,数据就会填充不上去,建议当数据为空时,给占位符填充空字符串。
往期推荐
分享
收藏
点赞
在看

夜雨聆风