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

求基于Apache POI 5.0.0的Java实现带非文本元素的.doc转PDF方案

基于Apache POI 5.0.0的.doc转PDF解决方案(保留图片、表格格式)

核心逻辑

Apache POI的HWPF组件仅负责读取.doc文档的内容与结构元数据,本身不处理排版布局。之前出现的“图片连续放置、文本后置”问题,是因为直接提取内容拼接时忽略了原文档的层级位置关系。要保留格式,需通过POI解析文档的段落、图片、表格的结构,再用PDF生成库(如iText 7)按原结构渲染PDF。

依赖配置(Maven)

需引入POI 5.0.0相关依赖和iText 7核心依赖:

<dependencies>
    <!-- Apache POI 5.0.0 处理.doc文件 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-scratchpad</artifactId>
        <version>5.0.0</version>
    </dependency>
    <!-- iText 7 生成PDF -->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itext-core</artifactId>
        <version>7.2.3</version>
        <type>pom</type>
    </dependency>
</dependencies>

实现代码示例

1. 完整转换类

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.*;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.layout.properties.TextAlignment;

import java.io.FileInputStream;
import java.io.IOException;

public class DocToPdfConverter {
    private static Document pdfDoc;

    public static void main(String[] args) throws IOException {
        // 初始化PDF文档
        initPdf("output.pdf");
        
        try (HWPFDocument doc = new HWPFDocument(new FileInputStream("input.doc"))) {
            Range range = doc.getRange();
            // 遍历文档所有段落,按顺序处理内容
            for (int i = 0; i < range.numParagraphs(); i++) {
                Paragraph poiPara = range.getParagraph(i);
                // 处理普通文本段落
                processParagraph(poiPara);
                // 处理段落内的图片
                processImagesInParagraph(poiPara, doc);
                // 处理表格
                if (poiPara.isInTable()) {
                    Table table = poiPara.getTable();
                    processTable(table);
                }
            }
        } finally {
            if (pdfDoc != null) {
                pdfDoc.close();
            }
        }
    }

    // 初始化PDF写入器
    private static void initPdf(String outputPath) throws IOException {
        PdfWriter writer = new PdfWriter(outputPath);
        PdfDocument pdf = new PdfDocument(writer);
        pdfDoc = new Document(pdf);
    }

    // 处理普通文本段落,映射原文档样式
    private static void processParagraph(Paragraph poiPara) throws IOException {
        String text = poiPara.text().trim();
        if (text.isEmpty()) return;

        CharacterRun run = poiPara.getCharacterRun(0);
        String fontName = run.getFontName();
        int fontSize = run.getFontSize() / 2; // POI字号为半磅值,转换为标准磅

        PdfFont font = PdfFontFactory.createFont(fontName, "UTF-8", true);
        Paragraph pdfPara = new Paragraph(text)
                .setFont(font)
                .setFontSize(fontSize)
                .setAlignment(mapAlignment(poiPara.getAlignment()));

        pdfDoc.add(pdfPara);
    }

    // 对齐方式映射:POI样式转iText样式
    private static TextAlignment mapAlignment(short poiAlign) {
        return switch (poiAlign) {
            case ParagraphAlignment.CENTER -> TextAlignment.CENTER;
            case ParagraphAlignment.RIGHT -> TextAlignment.RIGHT;
            default -> TextAlignment.LEFT;
        };
    }

    // 处理段落内的图片,按原位置插入PDF
    private static void processImagesInParagraph(Paragraph poiPara, HWPFDocument doc) throws IOException {
        for (int j = 0; j < poiPara.numCharacterRuns(); j++) {
            CharacterRun run = poiPara.getCharacterRun(j);
            if (run.isPartOfPicture()) {
                int picIndex = run.getPicOffset();
                Picture picture = doc.getPictureTable().extractPicture(picIndex, false);
                byte[] picBytes = picture.getContent();

                Image pdfImage = new Image(ImageDataFactory.create(picBytes))
                        .setWidth(picture.getWidth() / 15) // 缇转磅(1缇=1/15磅)
                        .setHeight(picture.getHeight() / 15);
                pdfDoc.add(pdfImage);
            }
        }
    }

    // 处理表格,保留行列结构
    private static void processTable(Table poiTable) throws IOException {
        int numCols = poiTable.numColumns();
        Table pdfTable = new Table(numCols);

        for (int rowIdx = 0; rowIdx < poiTable.numRows(); rowIdx++) {
            TableRow row = poiTable.getRow(rowIdx);
            for (int colIdx = 0; colIdx < row.numCells(); colIdx++) {
                TableCell cell = row.getCell(colIdx);
                String cellText = cell.text().trim();

                Cell pdfCell = new Cell().add(new Paragraph(cellText));
                pdfCell.setBorder(com.itextpdf.layout.properties.Border.NO_BORDER); // 可根据需求调整边框
                pdfTable.addCell(pdfCell);
            }
        }

        pdfDoc.add(pdfTable);
    }
}

注意事项

  • 上述代码为基础实现,针对页眉页脚、单元格合并、分栏等复杂样式,需额外扩展逻辑。
  • 确保运行环境存在原文档使用的字体,否则会用默认字体替代,可能导致格式偏差。
  • POI 5.0.0对较旧版本的.doc兼容性较好,极早期的.doc文档可能存在解析异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:23:19