package com.product.print.util; import cn.hutool.core.io.IoUtil; import com.deepoove.poi.exception.RenderException; import com.deepoove.poi.render.RenderContext; 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 org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalAlignRun; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.Map; 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 XWPFRun run; @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(); render(table, context.getData()); } catch (Exception e) { 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; } //获取fieldRow所在的下标 int fieldRowIndex = xwpfTable.getRows().indexOf(fieldRow); for (int i = 0; i < subTableData.size(); i++) { Map map = subTableData.get(i); //创建一行在fieldRowIndex下面 XWPFTableRow row = xwpfTable.insertNewTableRow(fieldRowIndex + 1 + i); copyTableRow(row, fieldRow); //设置row的属性与fieldRow一致 row.setHeight(fieldRow.getHeight()); //遍历字段每个字段创建一个单元格 for (int j = 0; j < fieldNames.length; j++) { //当前单元格 XWPFTableCell cell; //判断row中第j个单元格是否存在 if (row.getTableCells().size() > j) { cell = row.getCell(j); } else { cell = row.createCell(); } //设置单元格的值从map中取出 //判断是否是序号列 if (indexKey.equals(fieldNames[j])) { cell.setText(String.valueOf(i + 1)); continue; } Object value = map.get(fieldNames[j]); if (value == null) { value = ""; } String text = cell.getText(); //删除单元格中的旧内容 if (!StringUtils.isEmpty(text)) { List paragraphs = cell.getParagraphs(); if (paragraphs.size() > 1) { cell.removeParagraph(1); } List runs = paragraphs.get(0).getRuns(); //清空文字 for (int k = 0; k < runs.size(); k++) { runs.get(k).setText("", 0); } } cell.setText(value.toString()); if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j]) && !StringUtils.isEmpty(map.get("sign_attachment_uuid"))) { //意见框 插入签名图片到单元格右下角位置 XWPFParagraph 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(); } } } } //删除起始行 xwpfTable.removeRow(startRowIndex); //删除fieldRow xwpfTable.removeRow(fieldRowIndex - 1); // //读取完毕后删除最后一行 // xwpfTable.removeRow(rows.size() - 1); //获取所有的行判断单元格是否有值没有就删除该行 // List tableRows = xwpfTable.getRows(); // List deleteRows = new ArrayList<>(); // for (int i = 0; i < tableRows.size(); i++) { // XWPFTableRow row = tableRows.get(i); // boolean flag = false; // for (int j = 0; j < row.getTableCells().size(); j++) { // XWPFTableCell cell = row.getTableCells().get(j); // if (StringUtils.isNotEmpty(cell.getText())) { // flag = true; // break; // } // } // if (!flag) { // deleteRows.add(row); // } // } // //遍历要删除的行获取所在的下标进行删除 // for (int i = 0; i < deleteRows.size(); i++) { // XWPFTableRow row = deleteRows.get(i); // int index = xwpfTable.getRows().indexOf(row); // xwpfTable.removeRow(index); // } // //遍历数据集合,每个map对应一行数据 // for (int i = 0; i < subTableData.size(); i++) { // Map map = subTableData.get(i); // //创建一行 // XWPFTableRow row = xwpfTable.createRow(); // //遍历字段每个字段创建一个单元格 // for (int j = 0; j < fieldNames.length; j++) { // //当前单元格 // XWPFTableCell cell; // //判断row中第j个单元格是否存在 // if (row.getTableCells().size() > j) { // cell = row.getTableCells().get(j); // } else { // cell = row.createCell(); // } // //设置单元格的值从map中取出 // //判断是否是序号列 // if (indexKey.equals(fieldNames[j])) { // cell.setText(String.valueOf(i + 1)); // continue; // } // Object value = map.get(fieldNames[j]); // if (value == null) { // value = ""; // } // //设置单元格的值 // cell.setText(value.toString()); // } // } } /** * 解析表达式中的值获取 以{{$开头,以}}结尾的字符串取出$后面的值截止到}} */ 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); } } } }