JAVA Apache POI解析docx格式的word文件並提取帶樣式文本

關於JAVA Apache POI讀取word文檔,網上資料很多,但是大多數還是僅僅提取文檔中的純文本,好一點的,也就提取所有圖片,但是,word文檔本身是具有樣式的,這樣簡單粗暴的提取就會丟失字體、字號、顏色、粗體、斜體等一系列樣式,也沒有辦法還原圖片在文檔流中的位置,沒有辦法提取出表格。

docx格式的word文件實際上是一個壓縮包,通過修改後綴名爲rar後可用winrar打開,裏面實際上是xml文件

這是因爲docx文件遵循了OfficeOpenXML規範,該規範內容很多,有興趣的同學可以自行下載翻閱。打開上圖的word文件夾

這裏我們需要關注的是document.xml和numbering.xml

其中document.xml包含了文檔的主要結構與主要文本內容,其形式有點像HTML語言

而numbering.xml與自動序號有關,由於非常複雜,文本暫不討論,主要原因是自動序號實際上是在渲染時進行實時計算的,並且需要有不同的層次。

在開始使用Apache POI解析之前,我們需要了解一些關於docx文件結構的基本概念

整個文檔是一個document,document的子元素爲Paragraph(段落)和Table(表格)這和我們日常使用word的經驗基本相符,Paragraph的子元素爲Run,代表一段連續的相同樣式的文本,一般來說,沒有改過樣式的一段純中文或者純英文就在一個Run內,而一旦對其中一個字改變了字號、顏色、粗細等樣式,那麼其本身及前後必然會被不同的Run分割。

在實際使用Apache POI中,又分爲High Level和Low Level兩種API,一般以XWPF作爲前綴的是High Level API,以CT爲前綴的是Low Level,High Level API使用更簡單,比較容易理解,但是目前只有比較常見的元素有,例如XWPFParagraph、XWPFRun。Low Level API實際上是對xml文件中標籤的簡單封裝,需要你對OfficeOpenXML規範有足夠的瞭解,例如CTP(Paragraph)、CTR(Run)。當然,Low Level API也不一定能滿足你的全部需求,這個時候你只能對着OfficeOpenXML文檔自行解析XML了。High Level API可以獲取到對應的Low Level API,例如,XWPFParagraph.getCTP()。

上圖是High Level API的類的主要關係圖,其中藍色的是class,綠色的是interface

List<XWPFParagraph> paragraphs = document.getParagraphs();
for(XWPFParagraph paragraph:paragraphs){
    List<XWPFRun> runs= paragraph.getRuns();
    for(XWPFRun run:runs){
        List<XWPFPicture> pictures=run.getEmbeddedPictures();
        String text=run.text();
    }
}

一般來說,我們使用XWPFParagraph、XWPFRun、XWPFPicture就能滿足絕大多數情況。由於圖片的情況較爲複雜,這裏能抽取的圖片是嵌入型的圖片,而襯於文字上/下方的圖片抽取較爲複雜,本文暫不討論。

下面就主要講講XWPFParagraph與XWPFRun的樣式相關API

1.對齊方式

 xwpfParagraph.getAlignment().getValue()

下表僅列出常用對齊方式

返回值 含義
1 左對齊
2 居中
3 右對齊

2.字體

 run.getFontFamily()

3.字號

run.getFontSize()

4.粗體

run.isBold()

5.斜體

run.isItalic()

6.下劃線

run.getUnderline().getValue()

下表僅列出常用下劃線

返回值 含義
1 單下劃線
3 雙下劃線

7.下標

run.getVerticalAlignment().equals(STVerticalAlignRun.Enum.forInt(3))

8.上標

run.getVerticalAlignment().equals(STVerticalAlignRun.Enum.forInt(2))

9.顏色

 run.getColor()

表格抽取相對較簡單,最終又回到XWPFParagraph

List<XWPFTableRow> xwpfTableRows = xwpfTable.getRows();
for (XWPFTableRow xwpfTableRow : xwpfTableRows) {
    List<XWPFTableCell> xwpfTableCells = xwpfTableRow.getTableCells();
    for (XWPFTableCell xwpfTableCell : xwpfTableCells) {
        List<XWPFParagraph> xwpfParagraphs = xwpfTableCell.getParagraphs();
    }
}

最後補充一些特殊情況,這些情況需要用到Low level API

1.oMath公式

office中公式的版本很亂,因爲涉及到歷史兼容原因,老版本的公式只能抽取爲圖片,最新版本的公式可以抽取爲xml

List<CTOMath> ctoMaths = xwpfParagraph.getCTP().getOMathList();

順帶說一句,oMath可以轉換爲mml再轉換爲Latex語言

2.特殊符號

有些特殊符號是用的某種字體中的某個字符,一般使用&#x unicode進行顯示

List<CTSym> ctSyms = run.getCTR().getSymList();
if (ctSyms.size() > 0) {
    String symHtml = "";
    for (CTSym ctSym : ctSyms) {
        String font = ctSym.getFont();
        byte[] chars = ctSym.getChar();
        String unicode = "";
        for (byte aChar : chars) {
            int realInt = aChar & 0xff;
            String hex = Integer.toHexString(realInt);
            if (hex.length() == 1) {
                hex = "0" + hex;
            }
        }
        unicode += hex;
    }
}

3.着重號

run.getCTR().getRPr() != null && run.getCTR().getRPr().getEm() != null && run.getCTR().getRPr().getEm().getVal().toString().equals("dot")

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章