| doc/print.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/product/print/config/CmnCode.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/product/print/service/PrintRealizeService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/product/print/service/ide/IPrintRealizeService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/product/print/util/CustomPictureRenderPolicy.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/product/print/util/DynamicTableRenderPolicy.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
doc/print.md
@@ -26,7 +26,7 @@ ### 子表标签 {{$~index~}}: 固定写法,输出子表行号 {{$~i~}}: 固定写法,输出子表行号 {{$字段名}}:输出子表中指定字段的值 ###  src/main/java/com/product/print/config/CmnCode.java
@@ -32,6 +32,9 @@ NOT_FIND_START_FLAG("表格中没有找到起始下标", 15), //子表格中没有找到对应的字段 NOT_FIND_CHILD_TABLE_FIELD("子表格中没有找到对应的字段", 16), GET_BEAN_FAIL("获取bean失败", 17), DEALT_FILE_PATH_IS_EMPTY("处理后的文件路径为空", 18), EXISTS_FILE_RECORD_BUT_NO_FILE("存在文件记录,但是未找到对应文件", 19), ; private String text; src/main/java/com/product/print/service/PrintRealizeService.java
@@ -10,10 +10,11 @@ import com.deepoove.poi.config.ConfigureBuilder; import com.deepoove.poi.data.TextRenderData; import com.deepoove.poi.data.style.Style; import com.deepoove.poi.render.RenderContext; import com.google.common.collect.Lists; import com.product.common.lang.StringUtils; import com.product.core.cache.DataPoolCacheImpl; import com.product.core.config.Global; import com.product.core.entity.DataEntity; import com.product.core.entity.DataTableEntity; import com.product.core.entity.FieldSetEntity; import com.product.core.exception.BaseException; @@ -30,17 +31,15 @@ import com.product.tool.flow.service.FlowDetailService; import com.product.util.BaseUtil; import com.product.util.SystemParamReplace; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.multipdf.PDFMergerUtility; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.*; import java.util.stream.Collectors; @@ -121,17 +120,32 @@ /** * 打印传输pdf流到前端 * * @param fse 打印数据 * @param fseOrDte 打印数据 * @param response 响应 * @param isConvertPdf 是否转换为pdf * @throws BaseException */ @Override public void print(FieldSetEntity fse, HttpServletResponse response, boolean isConvertPdf) throws BaseException { public void print(DataEntity fseOrDte, HttpServletResponse response, boolean isConvertPdf) throws BaseException { FieldSetEntity fse; DataTableEntity dte; //打印配置 FieldSetEntity printConfig = getPrintConfig(fse.getString("~" + CmnConst.PRINT_TEMP + "~")); FieldSetEntity printConfig; if (fseOrDte.isFieldsetEntity()) { fse = (FieldSetEntity) fseOrDte; printConfig = getPrintConfig(getPrintTemplateField(fse)); } else if (fseOrDte.isDataTableEntity()) { dte = (DataTableEntity) fseOrDte; fse = dte.getFieldSetEntity(0); printConfig = getPrintConfig(getPrintTemplateField(fse)); } else { throw new BaseException(CmnCode.GET_BEAN_FAIL); } //获取到替换后的文件路径 (pdf文件或者word文件) String tempPdfFilePath = replaceTemplateFileOut(printConfig, fse, isConvertPdf); String tempPdfFilePath = replaceTemplateFileOut(printConfig, fseOrDte, isConvertPdf); if (StringUtils.isEmpty(tempPdfFilePath)) { throw new BaseException(CmnCode.DEALT_FILE_PATH_IS_EMPTY); } //获取文件名 String fileName = BaseUtil.ifNull(printConfig.getString(CmnConst.PRINT_FILE_NAME), printConfig.getString(CmnConst.PRINT_NAME)); if (isConvertPdf) { @@ -146,10 +160,26 @@ // 设置自定义头Content-Disposition response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(fileName)); try ( OutputStream out = response.getOutputStream(); ) { try (OutputStream out = response.getOutputStream();) { if (tempPdfFilePath.contains(",")) { // 更高效的方式:直接从文件合并到输出流 PDFMergerUtility merger = new PDFMergerUtility(); merger.setDestinationStream(response.getOutputStream()); String[] tempPdfFilePathArr = tempPdfFilePath.split(","); for (int i = 0; i < tempPdfFilePathArr.length; i++) { String singleTempPdfFilePath = tempPdfFilePathArr[i]; File file = new File(singleTempPdfFilePath); if (file.exists()) { merger.addSource(file); // 直接添加文件源 } } // 执行合并 merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly()); } else { FileUtil.writeToStream(new File(tempPdfFilePath), out); } } catch (Exception e) { e.printStackTrace(); throw new BaseException(CmnCode.PRINT_CONTENT_FAIL, e); @@ -159,7 +189,22 @@ System.out.println(absolutePath); file.delete(); } } /** * 获取打印模板字段 * @param fse * @return */ private String getPrintTemplateField(FieldSetEntity fse) { if (FieldSetEntity.isEmpty(fse)) { return null; } String printTemplateField = fse.getString("~" + CmnConst.PRINT_TEMP + "~"); if (StringUtils.isEmpty(printTemplateField)) { printTemplateField = fse.getString("~print_template~"); } return printTemplateField; } /** @@ -183,12 +228,30 @@ * 替换模板文件并输出 * * @param printConf 打印配置 * @param fse 替换数据 * @param fseOrDte 替换数据 * @param isConvertPdf 是否转换为pdf * @return 替换后的文件路径 * @throws BaseException 异常 */ private String replaceTemplateFileOut(FieldSetEntity printConf, FieldSetEntity fse, boolean isConvertPdf) throws BaseException { private String replaceTemplateFileOut(FieldSetEntity printConf, DataEntity fseOrDte, boolean isConvertPdf) throws BaseException { FieldSetEntity fse = null; DataTableEntity dte = null; if (fseOrDte.isFieldsetEntity()) { fse = (FieldSetEntity) fseOrDte; } else if (fseOrDte.isDataTableEntity()) { dte = (DataTableEntity) fseOrDte; } else { throw new BaseException(CmnCode.GET_BEAN_FAIL); } if (fse != null) { dte = new DataTableEntity(); dte.addFieldSetEntity(fse); } List<String> localTempPathList = Lists.newArrayList(); if (!DataTableEntity.isEmpty(dte)) { for (int m = 0; m < dte.getRows(); m++) { fse = dte.getFieldSetEntity(m); Object[] fields = fse.getFields(); for (int i = 0; i < fields.length; i++) { @@ -244,14 +307,18 @@ if (wordTemp.exists()) { wordTemp.delete(); } return localTempPathPdf; localTempPathList.add(localTempPathPdf); } catch (Exception e) { throw new BaseException(CmnCode.CONVERT_PDF_ERROR, e); } finally { FileUtil.del(localTempPathWord); } } else { localTempPathList.add(localTempPathWord); } return localTempPathWord; } } return BaseUtil.collection2String(localTempPathList); } /** @@ -274,18 +341,6 @@ Map<String, DataTableEntity> subDataMap = dataFse.getSubData(); ConfigureBuilder config = Configure.builder(); config.addPlugin('@', new FlowOpinionRenderPolicy(flowOpinion)); config.addPlugin('$', new com.deepoove.poi.policy.DynamicTableRenderPolicy() { @Override public void doRender(RenderContext<Object> context) throws Exception { return; } @Override public void render(XWPFTable table, Object data) throws Exception { return; } }); config.addPlugin('&', new CustomPictureRenderPolicy()); config.buildGrammerRegex("(#)?([\\w\\u4e00-\\u9fa5]+)(\\.?[\\w\\u4e00-\\u9fa5\\|]*)*(#)?"); TableEmptyHandler tableEmptyHandler = new TableEmptyHandler(); src/main/java/com/product/print/service/ide/IPrintRealizeService.java
@@ -1,6 +1,6 @@ package com.product.print.service.ide; import com.product.core.entity.FieldSetEntity; import com.product.core.entity.DataEntity; import com.product.core.exception.BaseException; import javax.servlet.http.HttpServletResponse; @@ -16,11 +16,10 @@ /** * 打印 * @param fse 打印数据 * @param fseOrDte 打印数据 * @param response 响应 * @param isConvertPdf 是否转换为pdf * @throws BaseException */ void print(FieldSetEntity fse, HttpServletResponse response,boolean isConvertPdf) throws BaseException; void print(DataEntity fseOrDte, HttpServletResponse response, boolean isConvertPdf) throws BaseException; } src/main/java/com/product/print/util/CustomPictureRenderPolicy.java
@@ -8,9 +8,12 @@ import com.deepoove.poi.data.Pictures; import com.deepoove.poi.policy.PictureRenderPolicy; import com.deepoove.poi.render.RenderContext; import com.product.common.lang.StringUtils; import com.product.core.dao.BaseDao; import com.product.core.entity.FieldSetEntity; import com.product.core.spring.context.SpringMVCContextHolder; import com.product.file.service.FileManagerService; import com.product.print.config.CmnCode; import java.io.File; import java.io.FileInputStream; @@ -31,9 +34,29 @@ public PictureRenderData cast(Object source) throws Exception { FileManagerService fileManagerService = SpringUtil.getBean(FileManagerService.class); String templateUid = source.toString(); if (StringUtils.isEmpty(templateUid)) { return null; } int width = 0; int height = 0; if (templateUid.contains(" ")) { String[] infoArr = templateUid.split(" "); templateUid = infoArr[0]; width = Integer.parseInt(infoArr[1]); height = Integer.parseInt(infoArr[2]); } BaseDao baseDao = SpringUtil.getBean(BaseDao.class); FieldSetEntity attachmentFse = baseDao.getFieldSetEntity("product_sys_attachments", templateUid, false); // 遇错跳过,默认使用透明背景不拦截,记录日志到文件 try { fileManagerService.getFile(templateUid); } catch (Exception e) { attachmentFse = baseDao.getFieldSetEntityByFilter("product_sys_attachments", "attachment_title=?", new Object[]{"sys_sp_deal_透明背景"}, false); fileManagerService.getFile(attachmentFse.getUUID()); SpringMVCContextHolder.getSystemLogger().error(String.format("%s-附件uuid:%s", CmnCode.EXISTS_FILE_RECORD_BUT_NO_FILE.getText(), templateUid)); } String fileName = attachmentFse.getString("file_name"); String type = fileName.substring(fileName.lastIndexOf(".") + 1); File file = fileManagerService.getFile(attachmentFse); @@ -47,8 +70,12 @@ } else if ("svg".equals(type)) { imgType = PictureType.SVG; } if (width > 0 && height > 0) { return converter.convert(Pictures.ofStream(new FileInputStream(file), imgType).size(width, height).create()); } else { return converter.convert(Pictures.ofStream(new FileInputStream(file), imgType).create()); } } @Override public void doRender(RenderContext<PictureRenderData> context) throws Exception { src/main/java/com/product/print/util/DynamicTableRenderPolicy.java
@@ -1,12 +1,12 @@ 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.google.common.collect.Maps; import com.product.common.lang.StringUtils; import com.product.core.exception.BaseException; import com.product.print.config.CmnCode; @@ -17,7 +17,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.Base64; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,11 +32,10 @@ private String replaceKey; private final String indexKey = "~index~"; private final String indexKey = "~i~"; public DynamicTableRenderPolicy(String replaceKey) { this.replaceKey = replaceKey; } private List<MetaTemplate> metaTemplateList; @@ -64,6 +65,7 @@ @Override public void render(XWPFTable xwpfTable, Object data) throws Exception { try { if (xwpfTable == null) { return; } @@ -73,15 +75,14 @@ String tableExpression = "{{" + this.replaceKey + "}}"; //获取表格起始行和结束行 int startRowIndex = -1; rows: for (int i = 0; i < rows.size(); i++) { outer: 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; break outer; } } @@ -127,7 +128,9 @@ } if (subTableData != null && subTableData.size() > 0) { for (int i = 0; i < subTableData.size(); i++) { int rowCount; for (int i = 0, count = 0; i < subTableData.size(); i += rowCount, count++) { rowCount = 1; Map<String, Object> map = subTableData.get(i); XWPFTableRow row; if (i == 0) { @@ -135,11 +138,20 @@ } else { int fieldRowIndex = xwpfTable.getRows().indexOf(fieldRow); //创建一行在fieldRowIndex下面 row = xwpfTable.insertNewTableRow(fieldRowIndex + i); row = xwpfTable.insertNewTableRow(fieldRowIndex + count); PrintPoiUtil.copyTableRow(row, fieldRow); } //遍历字段每个字段创建一个单元格 for (int j = 0; j < fieldNames.length; j++) { String fieldName = fieldNames[j]; if (fieldName.contains(" ")) { int curIndex = Integer.parseInt(fieldName.substring(fieldName.indexOf(" ") + 1)); if (curIndex > 1 && curIndex > rowCount) { map = i + 1 == subTableData.size() ? Maps.newHashMap() : subTableData.get(i + 1); rowCount = curIndex; } fieldName = fieldName.substring(0, fieldName.indexOf(" ")); } //当前单元格 XWPFTableCell cell = row.getCell(j); //清空单元格内容 @@ -161,20 +173,20 @@ } } //判断是否是序号列 if (indexKey.equals(fieldNames[j])) { cell.setText(String.valueOf(i + 1)); if (indexKey.equals(fieldName)) { if (map.isEmpty()) { xwpfRun.setText("", 0); } else { xwpfRun.setText(String.valueOf(i + rowCount), 0); } continue; } Object value = map.get(fieldNames[j]); Object value = map.get(fieldName); if (value == null) { value = ""; } // if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j])) { // //将流程节点处理人加到意见栏中 // value = "[" + map.get("actual_person") + "] " + value; // } xwpfRun.setText(value.toString(), 0); if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j])) { if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldName)) { if (!StringUtils.isEmpty(map.get("sign_attachment_uuid"))) { //意见框 插入签名图片到单元格右下角位置 xwpfParagraph = cell.addParagraph(); @@ -217,6 +229,9 @@ } //删除起始行 xwpfTable.removeRow(startRowIndex); } catch (Exception e) { e.printStackTrace(); } } /**