You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Apache POI转DOCX到PDF时Shape/Image/带文本WordArt丢失问题求助

解决Apache POI转换DOCX到PDF时Shape、图片、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

火山引擎 最新活动