package com.product.print.util; import cn.hutool.core.collection.CollectionUtil; import com.deepoove.poi.exception.RenderException; import com.deepoove.poi.render.RenderContext; import com.deepoove.poi.template.ElementTemplate; import com.deepoove.poi.template.MetaTemplate; import com.deepoove.poi.template.run.RunTemplate; import com.deepoove.poi.util.TableTools; import com.product.common.lang.StringUtils; import com.product.core.exception.BaseException; import com.product.print.config.CmnCode; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @Author cheng * @Date 2023/6/27 15:50 * @Desc */ public class DynamicTableRenderPolicy extends com.deepoove.poi.policy.DynamicTableRenderPolicy { private String replaceKey; private final String indexKey = "~index~"; public DynamicTableRenderPolicy(String replaceKey) { this.replaceKey = replaceKey; } private List metaTemplateList; @Override public void doRender(RenderContext context) throws Exception { RunTemplate runTemplate = (RunTemplate) context.getEleTemplate(); XWPFRun run = runTemplate.getRun(); try { if (!TableTools.isInsideTable(run)) { throw new IllegalStateException( "The template tag " + runTemplate.getSource() + " must be inside a table"); } XWPFTableCell cell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody(); XWPFTable table = cell.getTableRow().getTable(); this.metaTemplateList = context.getTemplate().getElementTemplates(); ElementTemplate eleTemplate = context.getEleTemplate(); int index = metaTemplateList.indexOf(eleTemplate); this.metaTemplateList = metaTemplateList.subList(index + 1, metaTemplateList.size()); render(table, context.getData()); } catch (Exception e) { e.printStackTrace(); throw new RenderException("Dynamic render table error:" + e.getMessage(), e); } } @Override public void render(XWPFTable xwpfTable, Object data) throws Exception { if (xwpfTable == null) { return; } List> subTableData = (List>) data; List rows = xwpfTable.getRows(); //读取rows中的内容 String tableExpression = "{{" + this.replaceKey + "}}"; //获取表格起始行和结束行 int startRowIndex = -1; rows: for (int i = 0; i < rows.size(); i++) { XWPFTableRow row = rows.get(i); for (int j = 0; j < row.getTableCells().size(); j++) { XWPFTableCell cell = row.getTableCells().get(j); String text = cell.getText(); if (tableExpression.equals(text)) { startRowIndex = i; break rows; } } } if (startRowIndex == -1) { throw new BaseException(CmnCode.NOT_FIND_START_FLAG); } //在表格中查找子表字段以{{$开头}}以}}结尾的内容 String regex = "\\{\\{\\$[a-zA-Z0-9_]+\\}\\}"; Pattern pattern = Pattern.compile(regex); //字段所在行 XWPFTableRow fieldRow = null; for (int i = startRowIndex; i < rows.size(); i++) { XWPFTableRow row = rows.get(i); for (int j = 0; j < row.getTableCells().size(); j++) { XWPFTableCell cell = row.getTableCells().get(j); String text = cell.getText(); Matcher matcher = pattern.matcher(text); if (matcher.find()) { fieldRow = row; i = rows.size(); break; } } } if (fieldRow == null) { throw new BaseException(CmnCode.NOT_FIND_CHILD_TABLE_FIELD); } //读取最后行每个单元格的值,调用getReplaceKey方法获取表达式中的值 String[] fieldNames = new String[fieldRow.getTableCells().size()]; for (int i = 0; i < fieldRow.getTableCells().size(); i++) { XWPFTableCell cell = fieldRow.getTableCells().get(i); String text = cell.getText(); String replaceKey = getReplaceKey(text); //获取表达式中的值 if (StringUtils.isEmpty(replaceKey)) { //设置单元格为空值 replaceKey = ""; } fieldNames[i] = replaceKey; } if (subTableData != null && subTableData.size() > 0) { for (int i = 0; i < subTableData.size(); i++) { Map map = subTableData.get(i); XWPFTableRow row; if (i == 0) { row = fieldRow; } else { int fieldRowIndex = xwpfTable.getRows().indexOf(fieldRow); //创建一行在fieldRowIndex下面 row = xwpfTable.insertNewTableRow(fieldRowIndex + i); PrintPoiUtil.copyTableRow(row, fieldRow); } //遍历字段每个字段创建一个单元格 for (int j = 0; j < fieldNames.length; j++) { //当前单元格 XWPFTableCell cell = row.getCell(j); //清空单元格内容 XWPFParagraph xwpfParagraph = cell.addParagraph(); int index = cell.getParagraphs().indexOf(xwpfParagraph); PrintPoiUtil.copyParagraph(xwpfParagraph, cell.getParagraphs().get(0)); //设置段落文字对齐方式 xwpfParagraph.setAlignment(cell.getParagraphs().get(0).getAlignment()); //删除所有的run for (int k = xwpfParagraph.getRuns().size() - 1; k >= 0; k--) { xwpfParagraph.removeRun(k); } XWPFRun xwpfRun = xwpfParagraph.createRun(); PrintPoiUtil.copyRun(xwpfRun, cell.getParagraphs().get(0).getRuns().get(0)); //删除cell中的段落但排除新增的段落 for (int k = cell.getParagraphs().size() - 1; k >= 0; k--) { if (k < index) { cell.removeParagraph(k); } } //判断是否是序号列 if (indexKey.equals(fieldNames[j])) { cell.setText(String.valueOf(i + 1)); continue; } Object value = map.get(fieldNames[j]); if (value == null) { value = ""; } xwpfRun.setText(value.toString(), 0); if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j]) && !StringUtils.isEmpty(map.get("sign_attachment_uuid"))) { //意见框 插入签名图片到单元格右下角位置 xwpfParagraph = cell.addParagraph(); xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT); XWPFRun run = xwpfParagraph.createRun(); String signAttachmentBase64 = map.get("sign_attachment_uuid").toString(); //将base64转换字节流 byte[] bytes = Base64.getDecoder().decode(signAttachmentBase64.split(",")[1]); //将字节流转换为输入流 InputStream inputStream = new ByteArrayInputStream(bytes); try { //换行插入图片 run.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, "sign.png", Units.toEMU(50), Units.toEMU(20)); inputStream.close(); } catch (InvalidFormatException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } } } else { // 删除fieldRow int index = xwpfTable.getRows().indexOf(fieldRow); xwpfTable.removeRow(index); } //删除起始行 xwpfTable.removeRow(startRowIndex); } /** * 解析表达式中的值获取 以{{$开头,以}}结尾的字符串取出$后面的值截止到}} */ private String getReplaceKey(String text) { //使用正则解析表达式中的值获取 以{{$开头,以}}结尾的字符串取出$后面的值截止到}} Pattern pattern = Pattern.compile("\\{\\{\\$(.*?)\\}\\}"); Matcher matcher = pattern.matcher(text); if (matcher.find()) { return matcher.group(1); } return null; } /** * 复制行,从source到target * * @param target * @param source */ public void copyTableRow(XWPFTableRow target, XWPFTableRow source) { // 复制样式 if (source.getCtRow() != null) { target.getCtRow().setTrPr(source.getCtRow().getTrPr()); } // 复制单元格 for (int i = 0; i < source.getTableCells().size(); i++) { XWPFTableCell cell1 = target.getCell(i); XWPFTableCell cell2 = source.getCell(i); if (cell1 == null) { cell1 = target.addNewTableCell(); } copyTableCell(cell1, cell2); } } /** * 复制单元格,从source到target * * @param target * @param source */ public void copyTableCell(XWPFTableCell target, XWPFTableCell source) { // 列属性 if (source.getCTTc() != null) { target.getCTTc().setTcPr(source.getCTTc().getTcPr()); } // 删除段落 for (int pos = 0; pos < target.getParagraphs().size(); pos++) { target.removeParagraph(pos); } // 添加段落 for (XWPFParagraph sp : source.getParagraphs()) { XWPFParagraph targetP = target.addParagraph(); copyParagraph(targetP, sp); } } /** * 复制图片到target * * @param target * @param picture * @throws IOException * @throws InvalidFormatException */ public void copyPicture(XWPFRun target, XWPFPicture picture) throws IOException, InvalidFormatException { String filename = picture.getPictureData().getFileName(); InputStream pictureData = new ByteArrayInputStream(picture .getPictureData().getData()); int pictureType = picture.getPictureData().getPictureType(); int width = (int) picture.getCTPicture().getSpPr().getXfrm().getExt() .getCx(); int height = (int) picture.getCTPicture().getSpPr().getXfrm().getExt() .getCy(); // target.addBreak(); target.addPicture(pictureData, pictureType, filename, width, height); // target.addBreak(BreakType.PAGE); } /** * 复制段落,从source到target * * @param target * @param source */ public void copyParagraph(XWPFParagraph target, XWPFParagraph source) { // 设置段落样式 target.getCTP().setPPr(source.getCTP().getPPr()); // 移除所有的run for (int pos = target.getRuns().size() - 1; pos >= 0; pos--) { target.removeRun(pos); } // copy 新的run for (XWPFRun s : source.getRuns()) { XWPFRun targetrun = target.createRun(); copyRun(targetrun, s); } } /** * 复制RUN,从source到target * * @param target * @param source */ public void copyRun(XWPFRun target, XWPFRun source) { // 设置run属性 target.getCTR().setRPr(source.getCTR().getRPr()); // 设置文本 target.setText(source.text()); // 处理图片 List pictures = source.getEmbeddedPictures(); for (XWPFPicture picture : pictures) { try { copyPicture(target, picture); } catch (InvalidFormatException e) { logger.error("copyRun", e); } catch (IOException e) { logger.error("copyRun", e); } } } }