關於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")