项目:若依前后端分离项目

用户上传合同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;
    }

上传合同后,用户进行合同生成

@Override
    public 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;
    }

private 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()) {
                if (paragraph.getRuns().isEmpty()) {
                    continue;
                }
                StringBuilder sb = new StringBuilder();
                for (XWPFRun run : paragraph.getRuns()) {
                    String text = run.getText(0);
                    if (text != null) {
                        sb.append(text);
                    }
                }
                XWPFRun sourceRun = paragraph.getRuns().get(0);
                CTRPr rPrCopy = null;

                if (sourceRun.getCTR().getRPr() != null) {
                    CTRPr rPr = sourceRun.getCTR().getRPr();
                    rPrCopy = CTRPr.Factory.newInstance();
                    rPrCopy.set(rPr);
                }

                String replaced = sb.toString();
                for (Map.Entry<String, String> entry : axjfContractRecord.getTemplateParams().entrySet()) {
                    String key = "{{$." + entry.getKey() + "}}";
                    replaced = replaced.replace(key, entry.getValue());
                }
                int runCount = paragraph.getRuns().size();
                for (int i = runCount - 1; i >= 0; i--) {
                    paragraph.removeRun(i);
                }
                XWPFRun run = paragraph.createRun();
                run.getCTR().setRPr(rPrCopy);
                run.setText(replaced);
            }

            document.getTables().forEach(table -> table.getRows().forEach(row ->
                    row.getTableCells().forEach(cell ->
                            cell.getParagraphs().forEach(paragraph -> {
                                List<XWPFRun> runs = paragraph.getRuns();
                                if (runs.isEmpty()) return;
                                StringBuilder paragraphText = new StringBuilder();
                                List<Integer> runIndexes = new ArrayList<>();
                                for (int i = 0; i < runs.size(); i++) {
                                    String text = runs.get(i).getText(0);
                                    if (text != null) {
                                        paragraphText.append(text);
                                        for (int j = 0; j < text.length(); j++) {
                                            runIndexes.add(i);
                                        }
                                    }
                                }
                                String newText = paragraphText.toString();
                                for (Map.Entry<String, String> entry : axjfContractRecord.getTemplateParams().entrySet()) {
                                    String key = "{{$." + entry.getKey() + "}}";
                                    newText = newText.replace(key, entry.getValue());
                                }
                                runs.forEach(run -> run.setText("", 0));
                                int textPos = 0;
                                while (textPos < newText.length()) {
                                    int runIndex = runIndexes.get(textPos);
                                    XWPFRun run = runs.get(runIndex);
                                    StringBuilder runText = new StringBuilder();
                                    while (textPos < newText.length() && runIndexes.get(textPos) == runIndex) {
                                        runText.append(newText.charAt(textPos));
                                        textPos++;
                                    }
                                    run.setText(runText.toString(), 0);
                                }
                            })
                    )
            ));

            document.write(fos);
        }
        return Constants.RESOURCE_PREFIX + newFile;
    }

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐