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

