Apache POI转DOCX到PDF时Shape/Image/带文本WordArt丢失问题求助
我之前也踩过这个坑,fr.opensagres的XWPF PDF转换器对Word里的浮动元素(比如置于文字下方的形状、图片,还有WordArt)支持确实有限——这些元素属于Word的DrawingML对象,转换器没完整解析它们的层级渲染逻辑,所以只会保留文本,丢了图形部分。下面给你几个可行的解决方案:
方案1:升级依赖版本尝试修复
先试试把Apache POI和转换器的版本往上提一提,新版本可能修复了部分DrawingML的解析问题:
更新你的Maven依赖为:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>fr.opensagres.poi.xwpf.converter.pdf</artifactId> <version>2.0.2</version> </dependency>
不过要提前有个心理预期:就算升级了,对于复杂的浮动布局(比如文字叠在图片/形状上),转换器可能还是没法完美支持,这是它的架构局限性导致的。
方案2:使用LibreOffice Headless模式转换(推荐)
如果能接受依赖外部工具,LibreOffice的无头模式是处理这类复杂Word格式最靠谱的方式——它对Word的所有元素(形状、WordArt、浮动图片)支持都很完整,转换效果和直接用LibreOffice打开DOCX再导出PDF几乎一样。
你可以在Java里通过调用命令行实现转换,示例代码如下:
import java.io.File; import java.io.IOException; public class LibreOfficeConverter { public static void convertDocxToPdf(String inputDocxPath, String outputPdfPath) throws IOException, InterruptedException { // 根据你的LibreOffice安装路径调整:Windows是soffice.exe,Linux/Mac是soffice String libreOfficeExecutable = "C:\\Program Files\\LibreOffice\\program\\soffice.exe"; ProcessBuilder processBuilder = new ProcessBuilder( libreOfficeExecutable, "--headless", // 无头模式,不启动GUI "--convert-to", "pdf", // 指定转换为PDF格式 "--outdir", new File(outputPdfPath).getParent(), // 设置输出目录 inputDocxPath // 输入DOCX文件路径 ); Process process = processBuilder.start(); int exitCode = process.waitFor(); if (exitCode == 0) { System.out.println("DOCX转PDF成功,输出路径:" + outputPdfPath); } else { System.err.println("转换失败,退出码:" + exitCode); } } public static void main(String[] args) throws IOException, InterruptedException { convertDocxToPdf("C:/TEMP/question.docx", "C:/TEMP/question.pdf"); } }
用这个方案需要先安装LibreOffice,并且确保soffice的路径正确。如果是Linux服务器,可以直接通过包管理器安装libreoffice-headless包。
方案3:自定义解析DrawingML元素(纯Java方案)
如果必须用纯Java实现,那你得手动解析XWPFDocument里的DrawingML对象,提取形状、图片、WordArt的信息,再用PDFBox或iText手动绘制到PDF中。这个方案复杂度很高,要处理大量Word格式细节:
- 提取形状的位置、大小、填充色、边框样式
- 提取图片的二进制数据和布局位置
- 解析WordArt的文本内容、字体、颜色和变换效果
举个简单的例子,提取DOCX中的图片并添加到PDF(需要结合PDFBox):
// 省略POI和PDFBox的初始化代码 for (XWPFParagraph paragraph : document.getParagraphs()) { for (XWPFRun run : paragraph.getRuns()) { for (XWPFPicture picture : run.getEmbeddedPictures()) { // 获取图片数据 byte[] pictureData = picture.getPictureData().getData(); // 在PDF中绘制图片(这里简化了位置计算,实际需要结合Word的布局信息) PDImageXObject image = PDImageXObject.createFromByteArray(pdfDocument, pictureData, "image"); contentStream.drawImage(image, 100, 700, image.getWidth()/2, image.getHeight()/2); } } }
但对于浮动元素(置于文字下方的图片/形状),你还得解析DrawingML的wp:position等属性来确定它们的层级和位置,这个过程非常繁琐,得深入了解Office Open XML的结构。
总结
如果追求转换的完整性和实现的简单性,**方案2(LibreOffice Headless)**是最优选择;如果必须纯Java实现,要么试试方案1的版本升级,要么就得投入精力做方案3的自定义解析。
内容的提问来源于stack exchange,提问作者Vighnesh




