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
相关产品推荐
相关产品推荐

