springboot实现合同生成
项目:若依前后端分离项目
用户上传合同docx,用户填写占位符,实现生成docx。
docx转pdf,pdf再转图片
代码不全,但是关键代码基本都在
依赖:
<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.9.1</version></dependency><dependency><groupId>org.docx4j</groupId><artifactId>docx4j</artifactId><version>6.1.0</version></dependency><dependency><groupId>org.docx4j</groupId><artifactId>docx4j-export-fo</artifactId><version>6.1.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.21</version></dependency><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.29</version></dependency>
上传合同,占位符为{{$.}}
新增合同的时候解析占位符
public int insertAxjfContractTemplate(AxjfContractTemplate axjfContractTemplate){axjfContractTemplate.setCreateTime(DateUtils.getNowDate());String fileUrl = axjfContractTemplate.getFileUrl();Set<String> fillFields;fileUrl = fileUrl.replace(Constants.RESOURCE_PREFIX, "");try {fillFields = extractPlaceholders(profile+fileUrl);} catch (Exception e) {throw new ServiceException("合同模板解析失败!");}axjfContractTemplate.setTemplateParams(JSON.toJSONString(fillFields));axjfContractTemplate.setUserId(SecurityUtils.getUserId());return axjfContractTemplateMapper.insertAxjfContractTemplate(axjfContractTemplate);}public static Set<String> extractPlaceholders(String filePath) throws Exception {Set<String> placeholders = new HashSet<>();FileInputStream fis = new FileInputStream(filePath);XWPFDocument doc = new XWPFDocument(fis);Pattern pattern = Pattern.compile("\\{\\$\\.(.+?)}}");for (XWPFParagraph paragraph : doc.getParagraphs()) {Matcher matcher = pattern.matcher(paragraph.getText());while (matcher.find()) {placeholders.add(matcher.group(1));}}for (XWPFTable table : doc.getTables()) {table.getRows().forEach(row ->row.getTableCells().forEach(cell -> {Matcher matcher = pattern.matcher(cell.getText());while (matcher.find()) {placeholders.add(matcher.group(1));}}));}doc.close();return placeholders;}
上传合同后,用户进行合同生成
@Overridepublic Map<String,String> insertAxjfContractRecord(AxjfContractRecord axjfContractRecord){axjfContractRecord.setCreateTime(DateUtils.getNowDate());axjfContractRecord.setUserId(SecurityUtils.getUserId());String fileUrl;String picUrl;try {fileUrl = handlerCreateContracr(axjfContractRecord);picUrl = handlerCreatePic(fileUrl.replace(Constants.RESOURCE_PREFIX, ""));} catch (Exception e) {e.printStackTrace();throw new ServiceException("生成失败!请稍后再试!");}axjfContractRecord.setDocxUrl(fileUrl);axjfContractRecord.setImgUrl(picUrl);threadPoolTaskExecutor.execute(() -> axjfContractRecordMapper.insertAxjfContractRecord(axjfContractRecord));Map<String, String> map = new HashMap<>();map.put("docxFileUrl",fileUrl);map.put("picUrl", picUrl);return map;}// word转pdf、pdf转图片private String handlerCreatePic(String fileUrl) throws Exception {WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(uploadBasePath + fileUrl));FOSettings foSettings = Docx4J.createFOSettings();foSettings.setWmlPackage(wordMLPackage);foSettings.setApacheFopMime("application/pdf"); // 指定输出类型Mapper fontMapper = new IdentityPlusMapper();String fontsDir = "Fonts";String[] fontFiles = {"simsun.ttc","CALIBRI.ttf"};for (String fontFile : fontFiles) {URL fontUrl = this.getClass().getClassLoader().getResource(fontsDir + "/" + fontFile);if (fontUrl != null) {String fontName = fontFile.split("\\.")[0];PhysicalFonts.addPhysicalFonts(fontName, fontUrl);fontMapper.put(fontName, PhysicalFonts.get(fontName));}}wordMLPackage.setFontMapper(fontMapper);ByteArrayOutputStream baos = new ByteArrayOutputStream();Docx4J.toFO(foSettings, baos, Docx4J.FLAG_EXPORT_PREFER_XSL);byte[] pdfBytes = baos.toByteArray();String newFile = "/contract/record/" + DateUtils.datePath() + "/" + IdUtils.fastUUID() + "_" + "合同图片.png";try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {PDFRenderer pdfRenderer = new PDFRenderer(document);List<BufferedImage> images = new ArrayList<>();int totalHeight = 0;int maxWidth = 0;for (int page = 0; page < document.getNumberOfPages(); page++) {BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 200);images.add(bim);totalHeight += bim.getHeight();maxWidth = Math.max(maxWidth, bim.getWidth());}BufferedImage longImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);Graphics2D g2d = longImage.createGraphics();int currentHeight = 0;for (BufferedImage img : images) {g2d.drawImage(img, 0, currentHeight, null);currentHeight += img.getHeight();}g2d.dispose();String outputPath = uploadBasePath + newFile;ImageIO.write(longImage, "png", new File(outputPath));}return Constants.RESOURCE_PREFIX + newFile;}// 填充wordprivate String handlerCreateContracr(AxjfContractRecord axjfContractRecord) throws IOException {AxjfContractTemplate axjfContractTemplate = axjfContractTemplateMapper.selectAxjfContractTemplateById(axjfContractRecord.getTemplateId());if (null == axjfContractTemplate){throw new ServiceException("合同模板不存在!刷新页面后重试");}String templateFile = axjfContractTemplate.getFileUrl();String newFile = "/contract/record/" + DateUtils.datePath() + "/" + IdUtils.fastUUID()+ "_"+ axjfContractTemplate.getTitle() + ".docx";// 上传文件路径String outputPath = uploadBasePath + newFile;File outFile = new File(outputPath);File parentDir = outFile.getParentFile();if (!parentDir.exists()) {boolean created = parentDir.mkdirs();if (!created) throw new RuntimeException("无法创建目录:" + parentDir.getAbsolutePath());}templateFile = uploadBasePath + templateFile.replace(Constants.RESOURCE_PREFIX, "");try (FileInputStream fis = new FileInputStream(templateFile);XWPFDocument document = new XWPFDocument(fis);FileOutputStream fos = new FileOutputStream(outFile)) {// 遍历段落for (XWPFParagraph paragraph : document.getParagraphs()) {for (XWPFRun run : paragraph.getRuns()) {String text = run.getText(0);if (text != null) {for (Map.Entry<String, String> entry : axjfContractRecord.getTemplateParams().entrySet()) {String key = "{{$." + entry.getKey() + "}}";if (text.contains(key)) {text = text.replace(key, entry.getValue());}}run.setText(text, 0);}}}document.getTables().forEach(table -> table.getRows().forEach(row ->row.getTableCells().forEach(cell ->cell.getParagraphs().forEach(paragraph ->paragraph.getRuns().forEach(run -> {String text = run.getText(0);if (text != null) {for (Map.Entry<String, String> entry : axjfContractRecord.getTemplateParams().entrySet()) {String key = "{{$." + entry.getKey() + "}}";if (text.contains(key)) {text = text.replace(key, entry.getValue());}}run.setText(text, 0);}})))));document.write(fos);}return Constants.RESOURCE_PREFIX + newFile;}