读取段落

  使用document.getParagraphs()可以获取所有段落,读取段落文本内容可以使用paragraph.getText()。
  但是细节的获取也很关键。以下是常见API:

API 用途
XWPFParagraph#getFirstLineIndent 获取首行缩进
XWPFRun#getColor 获取文字颜色
XWPFRun#getFontFamily 获取字体
XWPFRun#getFontSizeAsDouble 获取字体大小

以下是一个完整的例子:

public class Main {
    public static void main(String[] args) {
        try (FileInputStream stream = new FileInputStream("pages.docx")) {
            XWPFDocument document = new XWPFDocument(stream);
            List<XWPFParagraph> paragraphs = document.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                System.out.println(paragraph.getText());
                List<XWPFRun> runs = paragraph.getRuns();
                int firstLineIndent = paragraph.getFirstLineIndent();
                System.out.printf("段落首行缩进为"+firstLineIndent);
                for (XWPFRun run : runs) {
                    String color = run.getColor();
                    String fontFamily = run.getFontFamily();
                    Double fontSizeAsDouble = run.getFontSizeAsDouble();
                    System.out.println("文字颜色为" + color + ",字体为" + fontFamily+",字号"+fontSizeAsDouble);
                }
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

style数组为空的问题

如果没有显式设置字号,那么XWPFRun#getFontSizeAsDouble就会返回null。如果我们想从这个run的style中得到字体大小,在apache poi 5.0.0的代码中,是有bug的。代码位于XWPFRun#getStyle:

    public String getStyle() {
        CTRPr pr = getCTR().getRPr();
        if (pr == null) {
            return "";
        }

        CTString style = pr.getRStyleArray(0);
        if (style == null) {
            return "";
        }

        return style.getVal();
    }

运行时会触发空指针异常:

Exception in thread “main” java.lang.IndexOutOfBoundsException
at org.openxmlformats.schemas.wordprocessingml.x2006.main.impl.CTRPrImpl.getRStyleArray(CTRPrImpl.java:160)
at org.apache.poi.xwpf.usermodel.XWPFRun.getStyle(XWPFRun.java:1256)
at cn.edu.ncepu.Main.getFontSizeAsDouble(Main.java:55)
at cn.edu.ncepu.Main.main(Main.java:38)

但是在最新的版本5.2.4中已经修复了这个空指针的bug。但是获取了style之后依然难,因为word的style是一个继承关系,而且有很多默认值。

从rpr里获取字体字号

apache poi目前还不完善,没有直接从style中获取字号的api,所以我们直接从style的rpr元素中取。比如以下代码:

    private static Double getFontSize(XWPFStyle xwpfStyle) {
        CTRPr rPr = (xwpfStyle.getCTStyle()).getRPr();
        if (rPr.getSzArray().length > 0) {
            return ((BigInteger) rPr.getSzArray(0).getVal()).doubleValue();
        }
        return null;
    }

word的rpr里包含了字体的定义,。假如样式里定义了字体,那么就可以用rPr.getRFontsArray(0).getEastAsia()来获取字体。如果自身没有定义,那么字体字号的定义就来自继承的样式,可以使用xwpfStyle.getBasisStyleID()来获取继承的样式id。rpr非常复杂,下面我简单介绍一些常用的rpr属性:

POI API RPR XML元素 用途
rPr.getRFontsArray(0) <w:rFonts w:eastAsia=“隶书”/> 定义字体
rPr.getBArray() <w:b/> 字体加粗
rPr.getUArray() <w:u w:val=“single”/> 字体下划线
rPr.getIArray() <w:i/> 斜体
rPr.getColorArray(0) <w:color w:val=“EE0000”/> 字体颜色
rPr.getSzArray(0) <w:sz w:val=“28”/> 字号定义

之所以都是array,是因为word可以给不同的语言定义不同的字体,比如东亚文字和拉丁文字用不同的颜色。这样给编程增加了不少难度,同时也暴露了apache poi的诸多不足,封装太少了,需要深入研究底层xml。

读取图片

  读取word里的图片也不难了,只需要获取XWPFPictureData对象就可以了,然后就可以获取到图片内容的byte数组。但是需要注意的是图片是分类型的,不要拿到图片就存为PNG!以下是word支持的图片类型,图片类型在Document类中有静态常量定义:

格式 全称 说明与应用场景
EMF Enhanced Metafile Windows 增强型图元文件。矢量图形格式,放大不失真,常用于 Office 中的图表或从 Illustrator 等软件复制的矢量图。
WMF Windows Metafile Windows 图元文件。较老的矢量/光栅混合图形格式,常用于早期的剪贴画或简单的图形。
PICT Macintosh Picture Mac 绘图文件。主要用于早期的 Macintosh 系统,在跨平台文档中偶尔可见,包含矢量和位图数据。
JPEG Joint Photographic Experts Group 联合图像专家组格式。最常见的有损压缩格式,适用于照片、风景等色彩丰富的图片,文件体积较小。
PNG Portable Network Graphics 可移植网络图形。无损压缩格式,支持透明通道(Alpha),适用于网页图标、线条图或需要透明背景的图片。
DIB Device-Independent Bitmap 与设备无关的位图。通常指标准的 Windows 位图数据,不包含文件头信息,常在剪贴板操作中出现。
GIF Graphics Interchange Format 图形交换格式。支持 256 色,支持动画,常用于简单的网络表情包或小动画。
TIFF Tagged Image File Format 标签图像文件格式。一种灵活的位图格式,支持无损压缩,常用于出版印刷、扫描仪存档,文件通常较大。
EPS Encapsulated PostScript 封装的 PostScript。主要用于印刷和出版行业的矢量格式,包含一个低分辨率的预览图和高分辨率的打印指令。
BMP Bitmap 位图文件。Windows 原生格式,通常不压缩,画质高但文件体积巨大,适合在 Windows 内部进行快速渲染。
WPG WordPerfect Graphic WordPerfect 图元文件。一种较老的矢量图形格式,主要用于 Corel WordPerfect 文档,兼容性相对较差。

public static void main(String[] args) {
    try(FileInputStream stream = new FileInputStream("parse/pages.docx")) {
        XWPFDocument document = new XWPFDocument(stream);
        List<XWPFPictureData> allPictures = document.getAllPictures();
        for (XWPFPictureData pictureData: allPictures) {
            byte[] data = pictureData.getData();
            File file = new File(pictureData.getFileName());
            Files.write(file.toPath(), data);
        }
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

读取表格内容

  word中的表格,是XWPFTable-XWPFTableRow-XWPFTableCell的三级结构。有个这个三级结构,就非常好写代码获取了。

public static void main(String[] args) {
    try(FileInputStream stream = new FileInputStream("parse/table.docx")) {
        XWPFDocument document = new XWPFDocument(stream);
        List<XWPFTable> tables = document.getTables();
        for (XWPFTable table: tables) {
            List<XWPFTableRow> rows = table.getRows();
            for (XWPFTableRow row: rows) {
                List<XWPFTableCell> tableCells = row.getTableCells();
                for (XWPFTableCell cell: tableCells) {
                    System.out.print(cell.getText());
                    System.out.print("\t");
                }
                System.out.println();
            }
        }
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

页码

  实际工作中,解析word的场景少,生成word的场景多。但是如果有个需求是获取word特定一页的内容呢?比如说获取第9页内容,怎么办?可以说非常难实现,因为apache poi只能读取word底层的xml模型,实际的页码需要渲染才知道。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐