You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DOCX4j部署WAR包至Tomcat转PDF时空格丢失问题咨询

问题描述

使用DOCX4j编写代码实现DOCX转PDF功能时,本地IDE环境运行转换效果正常,生成的PDF可完整保留文档中的空格;但将项目打包为WAR包部署至Tomcat测试服务器后,转换生成的PDF出现空格丢失的异常。

项目相关依赖配置(pom.xml)
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.2</version>
</dependency>
     
<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j-JAXB-Internal -->
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-JAXB-Internal</artifactId>
    <version>8.3.7</version>
</dependency>
     
<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j-JAXB-ReferenceImpl -->
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
    <version>8.3.7</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j-JAXB-MOXy -->
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-JAXB-MOXy</artifactId>
    <version>8.3.7</version>
</dependency>


<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j-export-fo -->
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-export-fo</artifactId>
    <version>8.3.7</version>
</dependency>
DOCX转PDF核心实现代码
private byte[] docxToPdfBytes() {
    InputStream templateInputStream = null;
    try {
        templateInputStream = new FileInputStream(ApplicationConstants.TEMP_DOCX_PATH);
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(templateInputStream);
        FileOutputStream os = new FileOutputStream(ApplicationConstants.TEMP_PDF_PATH);
        Docx4J.toPDF(wordMLPackage, os);
        os.flush();
        os.close();
        return Files.readAllBytes(Paths.get(ApplicationConstants.TEMP_PDF_PATH));
    } catch (Throwable e) {
        logger.info(XrefSubscriberServiceService.class.getName() + "==> Method : docxToPdfBytes");
        logger.error(e.getMessage(), e);
    }
    finally {
        if(null != templateInputStream) {
            try {
                templateInputStream.close();
            } catch (IOException e) {
                logger.info(XrefSubscriberServiceService.class.getName() + "==> Method : docxToPdfBytes");
                logger.error(e.getMessage(), e);
            }
        }
    }
    return null;
}
问题根因
  • JAXB依赖冲突是核心诱因:DOCX4j提供的三个JAXB实现包(docx4j-JAXB-Internal、docx4j-JAXB-ReferenceImpl、docx4j-JAXB-MOXy)是互斥的,不能同时引入。本地IDE运行时类加载优先级固定,会加载到匹配的JAXB实现;但Tomcat的WebAppClassLoader类加载优先级和本地环境存在差异,会随机加载其中一个JAXB实现,一旦加载到不匹配的版本,解析DOCX内部的XML文件时,会忽略节点上的xml:space="preserve"标记,自动裁剪文本节点中的连续空格,最终导出的PDF就会出现空格丢失。
  • 空白裁剪配置被意外覆盖:DOCX4j本身提供了文档加载时自动裁剪空白的开关,默认状态为关闭。但如果存在类路径冲突,该配置可能被覆盖为开启状态,直接过滤掉文档中的空格字符。
  • 服务器字体缺失:如果Tomcat所在服务器没有安装DOCX文档中使用的字体,DOCX4j做字体映射替换时,可能将半角/全角空格的字符宽度计算为0,视觉上表现为空格丢失。
解决方案
  • 清理冗余JAXB依赖:删除pom文件中的docx4j-JAXB-Internal和docx4j-JAXB-MOXy两个依赖,仅保留docx4j-JAXB-ReferenceImpl,从根源上避免多JAXB实现导致的类加载冲突。
  • 显式强制关闭空白裁剪:在调用WordprocessingMLPackage.load()方法前,添加配置固定空白处理逻辑,不受类加载环境影响:
    // 强制关闭文档加载时的空白字符自动裁剪逻辑
    Docx4jProperties.setProperty("docx4j.openpackaging.packages.WordprocessingMLPackage.StripWhitespaceOnLoad", false);
    
  • 校验服务器字体环境:将DOCX模板中用到的所有字体(如宋体、微软雅黑、等线等常用字体)安装到Tomcat所在服务器的系统字体目录(Windows路径为C:\Windows\Fonts,Linux路径为/usr/share/fonts),安装完成后重启Tomcat生效,避免字体映射异常导致的空格宽度计算错误。
  • 补全Tomcat启动参数:修改Tomcat启动配置,在CATALINA_OPTS中添加JVM参数,放开XML解析的外部资源访问限制,避免JDK默认安全限制导致DOCX的XML解析规则加载失败:
    CATALINA_OPTS="$CATALINA_OPTS -Djavax.xml.accessExternalSchema=all -Djavax.xml.accessExternalDTD=all"
    

内容的提问来源于stack exchange,提问作者Queetu_247

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 04:12:21