Java读取Word文本框中的文本和图片的实践指南

在Java中处理Word文档(特别是读取文本框中的文本和图片)是一个常见的需求,尤其在文档自动化、内容提取和数据分析场景中。本文将详细介绍如何使用Apache POI和docx4j这两个主流库来读取Word文档(.docx格式)中文本框内的文本和图片。

图片[1]_Java读取Word文本框中的文本和图片的实践指南_知途无界

一、Word文档结构基础

Word的.docx文件本质是一个ZIP压缩包,内部包含XML文件(如document.xmlword/media/中的图片文件)和关系定义(_rels/文件夹)。
文本框在Word中属于“浮动形状”(Floating Shape),通常嵌套在文档的“绘图层”中(通过wp:inlinewp:anchor标签定义)。
图片可能直接嵌入文本框内,或作为文本框的装饰元素,存储在文档的media目录下,并通过关系(Relationship)与文本内容关联。


二、方案一:使用Apache POI(适合基础需求)

Apache POI是Java生态中最常用的Office文档处理库,但对复杂元素(如文本框内的图片)支持有限,更适合提取文本框中的纯文本

1. 添加依赖

Maven项目中添加以下依赖:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.2.4</version> <!-- 使用最新稳定版 -->
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.4</version>
</dependency>

2. 读取文本框中的文本

文本框在POI中通过XWPFSDT(Structured Document Tag)或绘图对象(XWPFPicture关联的形状)实现,但更常见的是通过文档的“绘图层”中的CTInlineCTAnchor元素定位。
以下代码演示如何遍历文档中的所有“段落”和“文本框形状”(简化版,实际需处理复杂结构):

import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlObject;
import java.io.FileInputStream;
import java.util.List;

public class PoiReadTextBoxText {
    public static void main(String[] args) throws Exception {
        try (FileInputStream fis = new FileInputStream("example.docx");
             XWPFDocument doc = new XWPFDocument(fis)) {
            
            // 遍历所有段落(部分文本框文本可能出现在段落中)
            for (XWPFParagraph para : doc.getParagraphs()) {
                System.out.println("段落文本: " + para.getText());
            }

            // 遍历所有表格(文本框可能嵌套在表格中)
            for (XWPFTable table : doc.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        for (XWPFParagraph p : cell.getParagraphs()) {
                            System.out.println("表格单元格文本: " + p.getText());
                        }
                    }
                }
            }

            // 注意:POI对直接提取文本框(浮动形状)的文本支持较弱,需通过底层XML解析(见下文补充)
        }
    }
}

补充:通过底层XML解析文本框文本(高级)

若需直接提取文本框(浮动形状)中的文本,需操作POI的底层XML对象(CTDrawingCTInline等)。示例代码:

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.apache.xmlbeans.XmlCursor;

// 在遍历段落时检查是否包含绘图对象(文本框)
for (XWPFParagraph para : doc.getParagraphs()) {
    for (XWPFRun run : para.getRuns()) {
        CTDrawing drawing = run.getCTR().getDrawingArray(0); // 获取第一个绘图对象
        if (drawing != null) {
            // 解析绘图对象中的文本(需进一步处理CTInline/CTPicture)
            XmlCursor cursor = drawing.newCursor();
            while (cursor.hasNextToken()) {
                // 此处需根据实际XML结构提取文本(复杂,建议用docx4j替代)
            }
        }
    }
}

局限性​:POI对文本框内图片的提取几乎无直接支持,且文本框的XML结构复杂,推荐优先使用docx4j。


三、方案二:使用docx4j(推荐,支持文本框文本和图片)

docx4j是专为处理.docx文档设计的高级库,提供了对文本框、图片等复杂元素的完整支持,能更简单地提取文本框内的文本和图片。

1. 添加依赖

Maven项目中添加:

<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-core</artifactId>
    <version>11.4.4</version> <!-- 使用最新版 -->
</dependency>
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j-export-fo</artifactId>
    <version>11.4.4</version>
</dependency>

2. 读取文本框中的文本

docx4j通过org.docx4j.wml.Drawingorg.docx4j.dml.wordprocessingDrawing.Inline等类定位文本框,并提取其中的文本节点。

完整代码示例:

import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import java.io.File;
import java.util.List;

public class Docx4jReadTextBox {
    public static void main(String[] args) throws Exception {
        // 加载Word文档
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("example.docx"));
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();

        // 遍历文档中的所有段落
        List<Object> content = documentPart.getContent();
        for (Object obj : content) {
            if (obj instanceof P) { // P表示段落
                P paragraph = (P) obj;
                for (Object runObj : paragraph.getContent()) {
                    if (runObj instanceof R) { // R表示运行(Run)
                        R run = (R) runObj;
                        for (Object drawingObj : run.getContent()) {
                            if (drawingObj instanceof Drawing) { // Drawing可能包含文本框或图片
                                Drawing drawing = (Drawing) drawingObj;
                                // 解析绘图对象中的文本框内容
                                extractTextBoxText(drawing);
                            }
                        }
                    }
                }
            }
        }
    }

    // 提取文本框中的文本
    private static void extractTextBoxText(Drawing drawing) {
        if (drawing.getInlineArray().length > 0) {
            Inline inline = drawing.getInlineArray(0); // 获取第一个内联绘图对象(文本框)
            // 文本框的文本通常存储在<pict>或<t>标签中(需进一步解析)
            // 此处简化:通过docx4j的辅助方法获取文本
            String text = getTextFromDrawing(inline);
            if (text != null && !text.trim().isEmpty()) {
                System.out.println("文本框文本: " + text);
            }
        }
    }

    // 辅助方法:从绘图对象中提取文本(简化实现)
    private static String getTextFromDrawing(Inline inline) {
        // 实际需解析inline的XML结构(通常包含<t>标签)
        // 这里使用docx4j的XmlUtils和XPath简化(需引入相关依赖)
        try {
            org.w3c.dom.Node node = inline.getDomNode();
            // 查找<t>标签(文本节点)
            // 注:实际代码需使用XPath或遍历DOM节点(示例省略复杂逻辑)
            return "从绘图对象提取的文本(需实现具体解析)";
        } catch (Exception e) {
            return null;
        }
    }
}

更简单的文本框文本提取(推荐使用docx4j的org.docx4j.TextUtils

docx4j提供了更高层的工具类TextUtils,可简化文本提取(但可能仍需处理文本框的特殊情况):

import org.docx4j.TextUtils;
import org.docx4j.wml.ContentAccessor;

// 遍历文档内容并提取所有文本(包括文本框)
String allText = TextUtils.extractText(documentPart.getContent());
System.out.println("文档所有文本(含文本框): " + allText);

注意​:此方法会提取文档内所有文本(包括文本框),但无法区分文本框与其他部分的文本。若需精确提取文本框文本,仍需解析绘图对象。

3. 读取文本框中的图片

图片在Word中通过org.docx4j.wml.Drawing关联的org.docx4j.dml.Graphicorg.docx4j.dml.blipFill.Blip定义,存储在文档的media/目录下。

完整代码示例:

import org.docx4j.openpackaging.parts.Part;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.relationships.Relationship;
import java.io.FileOutputStream;
import java.util.List;

// 在遍历Drawing对象时提取图片
private static void extractImagesFromDrawing(Drawing drawing) {
    if (drawing.getInlineArray().length > 0) {
        Inline inline = drawing.getInlineArray(0);
        // 获取绘图对象的关联关系(Blip指向图片)
        if (inline.getGraphic() != null) {
            org.docx4j.dml.Graphic graphic = inline.getGraphic();
            // 解析Blip(图片引用)
            // 此处简化:通过docx4j的辅助方法获取图片数据
            byte[] imageData = getImageDataFromDrawing(inline);
            if (imageData != null) {
                try (FileOutputStream fos = new FileOutputStream("extracted_image.png")) {
                    fos.write(imageData);
                    System.out.println("图片已保存为 extracted_image.png");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

// 辅助方法:从绘图对象中提取图片数据(简化实现)
private static byte[] getImageDataFromDrawing(Inline inline) {
    try {
        // 获取Blip(图片引用)
        org.docx4j.dml.blipFill.Blip blip = inline.getGraphic().getGraphicData()
                .getAny().stream()
                .filter(obj -> obj instanceof org.docx4j.dml.blipFill.BlipFillProperties)
                .map(obj -> (org.docx4j.dml.blipFill.BlipFillProperties) obj)
                .findFirst()
                .orElse(null)
                .getBlipArray(0);
        
        if (blip != null) {
            String blipId = blip.getEmbed(); // 获取图片的关联ID
            // 通过文档的关系部分找到图片的实际数据
            RelationshipsPart rp = inline.getPart().getRelationshipsPart();
            Relationship rel = rp.getRelationship(blipId);
            Part imagePart = rp.getPart(rel);
            return imagePart.getInputStream().readAllBytes();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

更简单的图片提取(推荐使用docx4j的org.docx4j.export.PictureExtractor

docx4j提供了专门的图片提取工具类,可自动遍历文档中的所有图片(包括文本框内的图片):

import org.docx4j.export.PictureExtractor;

// 创建图片提取器
PictureExtractor pictureExtractor = new PictureExtractor(wordMLPackage);
List<org.docx4j.openpackaging.parts.Part> images = pictureExtractor.extractImages();

// 保存所有图片
int index = 1;
for (Part imagePart : images) {
    String fileName = "extracted_image_" + index + ".png";
    try (FileOutputStream fos = new FileOutputStream(fileName)) {
        fos.write(imagePart.getInputStream().readAllBytes());
        System.out.println("图片已保存: " + fileName);
    } catch (Exception e) {
        e.printStackTrace();
    }
    index++;
}

注意​:此方法会提取文档内所有图片(包括文本框内和非文本框内的图片),但无法直接区分图片所属的文本框。若需精确提取文本框内的图片,需结合绘图对象的解析。


四、综合示例:完整提取文本框文本和图片(docx4j)

以下代码整合了文本框文本和图片的提取逻辑:

import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.export.PictureExtractor;
import java.io.File;
import java.util.List;

public class Docx4jFullExample {
    public static void main(String[] args) throws Exception {
        // 1. 加载文档
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("example.docx"));
        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();

        // 2. 提取所有文本(含文本框)
        String allText = org.docx4j.TextUtils.extractText(documentPart.getContent());
        System.out.println("文档所有文本(含文本框): " + allText);

        // 3. 提取所有图片(含文本框内的图片)
        PictureExtractor pictureExtractor = new PictureExtractor(wordMLPackage);
        List<org.docx4j.openpackaging.parts.Part> images = pictureExtractor.extractImages();
        int imgIndex = 1;
        for (org.docx4j.openpackaging.parts.Part imagePart : images) {
            String fileName = "extracted_image_" + imgIndex++ + ".png";
            try (java.io.FileOutputStream fos = new java.io.FileOutputStream(fileName)) {
                fos.write(imagePart.getInputStream().readAllBytes());
                System.out.println("图片已保存: " + fileName);
            }
        }

        // 4. 精确提取文本框文本和图片(需解析绘图对象,见前文补充)
    }
}

五、注意事项与常见问题

  1. 文本框的复杂性​:
    Word中的文本框可能是浮动形状(Floating Shape)或嵌入式形状(Inline Shape),且可能嵌套在其他元素(如表格、页眉页脚)中。需遍历文档的所有部分(段落、表格、页眉等)。
  2. 图片存储位置​:
    图片通常存储在文档的word/media/目录下(通过ZIP解压可见),并通过关系(Relationship)与绘图对象关联。提取时需通过Blipembed属性找到对应的图片部分。
  3. 库的选择建议​:
    • 若只需提取文本框中的纯文本,且文档结构简单,优先使用Apache POI(简单易用)。
    • 若需提取文本框中的文本和图片,或处理复杂结构(如嵌套文本框),强烈推荐docx4j(功能更强大,但学习曲线稍陡)。
  4. 调试技巧​:
    • 使用工具(如7-Zip)解压.docx文件,直接查看word/document.xmlword/media/目录,理解文档结构。
    • 打印绘图对象(Drawing)的XML内容(通过System.out.println(inline.toString())),辅助定位文本和图片的关联关系。

通过上述方法,你可以灵活地在Java中提取Word文本框中的文本和图片,满足文档处理、内容迁移等实际需求。根据项目复杂度选择合适的库(POI或docx4j),并注意处理文档结构的多样性。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞52 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容