Word模版生成Pdf

大家好,最近遇见了一个需求,需要根据一个特定的Word模版,生成不同的Pdf报表,心想这不是一个简单的需求吗?还不是手到擒来,但是最后到底是经验不足了,踩坑了,到底是任何需求都不能小看呀!

初始:直接使用word模版渲染,转化为pdf文件

在刚开始的时间,我采用了先进行word的模版的渲染,然后通过将word转为pdf的方案:

渲染:

  • 采用poi-tl 库进行word模版的渲染,这个库是一个不错的关于操作word模版的第三方库,如果你的需求是生成word文档,那么它将是一个不错的选择
xml 复制代码
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.10.3</version>
</dependency>
  • 在这个示例代码中,进行了模版文件的加载,然后调用render 函数,传入上下文对象,最终可以将渲染的结果写入一个输出流
java 复制代码
FileInputStream fileInputStream = new FileInputStream(filePath);
XWPFTemplate template = XWPFTemplate.compile(fileInputStream).render(
        contentMap);
bos = new ByteArrayOutputStream();
template.write(bos);
byte[] byteArray = bos.toByteArray(); // 生成的文件数组
template.close();
bos.close();

转化:

在本方案中,采用了documents4j 这个第三方库,它可以通过几行代码 很轻松的实现word转pdf,但是缺点在于转化的速度比较慢(一页的word文档转化花费了4s的时间),并且只能在windows平台下使用,无法在linux平台下使用。

xml 复制代码
<!--        word转pdf需要的内容-->
        <dependency>
            <groupId>com.documents4j</groupId>
            <artifactId>documents4j-local</artifactId>
            <version>1.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.documents4j</groupId>
            <artifactId>documents4j-transformer-msoffice-word</artifactId>
            <version>1.0.3</version>
        </dependency>
        
java 复制代码
IConverter converter = LocalConverter.builder().build();
converter.convert(bis).as(DocumentType.DOCX).to(response.getOutputStream()).as(DocumentType.PDF).execute();

官方的解释,它利用 Microsft OfficeAPIs 来进行文档转换,如果想要使用的话,需要在linux中安装**OpenOffice/LibreOffice** 这是网络上给出的可行的方案,但是对应的博客评论区的小伙伴也是反应无法适用,但是我我并未进行尝试

总结

如果你的最后生成结果是一个word文档,那么建议选择poi-tl来完成你的业务逻辑,如果需要生成pdf文件,切服务器环境是windows可以选择这种方案。

注意:word转pdf的第三方库中有效果较好的Aspose库, 但是是收费的,破解版需要的同学可网络上自行寻找,不建议使用到商业项目中, 减少不必要的麻烦

中期:将word模版转为html,后续根据html生成pdf文件

渲染:word转html

采用poi 实现word转html,使用过程中,简单的word文件,可以实现较好的转化效果,但是复杂的文件,带表格等复杂格式的文件, 无法较好的转化完成,出现了样式丢失的情况

xml 复制代码
<!-- 以下组件为html转pdf需要的内容-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
            <!--            <version>3.16</version>-->
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.1.2</version>
            <!--            <version>3.16</version>-->
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>

        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>xdocreport</artifactId>
            <version>2.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.4</version>
        </dependency>
  • 以下示例代码仅适用于word2007版本的文件(.docx)
java 复制代码
 public static String Word2007ToHtml(InputStream input)
            throws IOException {
        XWPFDocument document = new XWPFDocument(input);
        // 2) 解析 XHTML配置 (这里设置IURIResolver来设置图片存放的目录)
        XHTMLOptions options = XHTMLOptions.create();
        Map<String, String> imgMap = new HashMap<>();
//        String preUrl = fileUtil.getPreUrl();
        options.setExtractor(new IImageExtractor() {
            @Override
            public void extract(String imagePath, byte[] imageData) throws IOException {
                // 生成文件名
                String substring = imagePath.substring(imagePath.lastIndexOf("."));
//                保存文件拼接url路径
                InputStream inputStream = new ByteArrayInputStream(imageData);
//                FileUtils.saveFile(inputStream,,substring);
//                todo 与上传组件有所关联
//              得到图片的路径
                String imageurl = "";
//              保存到静态文件目录下
                imgMap.put(imagePath, imageurl);
            }
        });
        // html中图片的路径 相对路径
        options.URIResolver(new IURIResolver() {
            @Override
            public String resolve(String uri) {
                //设置图片路径
                return imgMap.get(uri);
            }
        });

        options.setIgnoreStylesIfUnused(false);
        options.setFragment(true);

        // 3) 将 XWPFDocument转换成XHTML
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

//        XHTMLConverter.getInstance().convert(document, baos, options);
        XHTMLConverter.getInstance().convert(document, baos, options);
//        String content = new String(baos.toByteArray(),"gbk");
        String content = baos.toString(StandardCharsets.UTF_8.name());

//        org.jsoup.nodes.Document parse = Jsoup.parse(content);
        System.out.println(content);
        System.out.println("------------------------------------");
        return content;
    }

转化:html转pdf

  • 对于html转pdf的需求网络中有较多的第三方库,并且效果比直接word转pdf的要好,在此也给出我的代码
xml 复制代码
<dependency>
    <groupId>com.lowagie</groupId>
    <artifactId>itext</artifactId>
    <version>2.1.7</version>
</dependency>
java 复制代码
 private static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    /**
* 设置BaseFont
*
*  @param  fontPath 字体路径
*  @return
 */
private static ConverterProperties creatBaseFont(String fontPath) throws IOException {
        ClassPathResource resource = null;
//        if(StrUtil.isBlank(fontPath)) {
        ClassPathResource classPathResource = new ClassPathResource("table/STSONG.TTF");
        InputStream resourceAsStream = classPathResource.getInputStream();

//        InputStream resourceAsStream = Pdf7Kit.class.getClassLoader().getResourceAsStream("table/STSONG.TTF");
//        resource = new ClassPathResource("");
//        }

//         FileInputStream fileInputStream = new FileInputStream("E:\javaProject2023\UndergraduateManagementSystem\UndergraduateManagementSystemBackend\NascentManagementSystem\src\main\resources\table\STSONG.TTF");
        ConverterProperties properties = new ConverterProperties();

        FontProvider fontProvider = new DefaultFontProvider();
        FontProgram fontProgram;
        try {
            fontProgram = FontProgramFactory.createFont(Pdf7Kit.toByteArray(resourceAsStream));
//            fontProgram = FontProgramFactory.createFont(Pdf7Kit.toByteArray(fileInputStream));
            fontProvider.addFont(fontProgram);
            properties.setFontProvider(fontProvider);

        } catch (IOException e) {
            log.error("creat base font erro", e);
        }
        return properties;
    }
    
    
    
    /**
    * 根据html转为pdf
    **/
     public static void creatPdfBycontent(String htmlContent, String pdfPath, String fontPath) throws IOException {
        if (StrUtil.isBlank(htmlContent) || StrUtil.isBlank(pdfPath)) {
            log.warn("html2pdf fail. htmlPath or pdfPath is null .");
            return;
        }
        // 拼接html路径
//        String src = htmlPath;

        ConverterProperties properties = creatBaseFont(fontPath);
        File file = new File(pdfPath);
        if (!file.exists()) {
            File parentFile = file.getParentFile();
            if (parentFile != null && !parentFile.exists()) {
                parentFile.mkdirs();
            }
            file.createNewFile();
            HtmlConverter.convertToPdf(htmlContent, new FileOutputStream(file), properties);

        }
    }

总结

在本方案中,通过将word转pdf的过程变为 word-html-pdf,但是也存在一些问题, word转html的复杂情况下不是太好,建议通过引入富文本编辑器 ,手动调整html模版的格式

最后:直接制作pdf模版的方式

  • 直接制作pdf模版的方式,优点在于代码简单,缺点在于pdf模版的制作比较复杂,且无法动态添加表格,只能完成简单的数据填充
  • 采用Java中的itext库进行渲染

参考文章: blog.csdn.net/weixin_4420...

总结

格式准确,实现效果较好,缺点在于无法添加动态表格,适用于固定情况下的模板渲染

最终方案

综合以上方案,因为是客户对于服务器环境没有要求,并且在Word模版的渲染过程中有动态表格的存在,最终决定采用方案一,线上服务器环境采用Windows Server版本。

注:如果大佬们有其他更好的方案,还请指教。文章如有错误之处,还请指出

相关推荐
这孩子叫逆6 分钟前
Spring Boot项目的创建与使用
java·spring boot·后端
coderWangbuer1 小时前
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
spring boot·后端·sql
攸攸太上1 小时前
JMeter学习
java·后端·学习·jmeter·微服务
Kenny.志1 小时前
2、Spring Boot 3.x 集成 Feign
java·spring boot·后端
sky丶Mamba2 小时前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端
千里码aicood3 小时前
【2025】springboot教学评价管理系统(源码+文档+调试+答疑)
java·spring boot·后端·教学管理系统
程序员-珍3 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
liuxin334455663 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
数字扫地僧4 小时前
HBase与Hive、Spark的集成应用案例
后端
架构师吕师傅4 小时前
性能优化实战(三):缓存为王-面向缓存的设计
后端·微服务·架构