注:有不正確的地方還望大神能夠指出,抱拳了 老鐵!
參考 API:http://poi.apache.org/apidocs/org/apache/poi/xwpf/usermodel/XWPFDocument.html
主要參考文章 1:http://www.cnblogs.com/Springmoon-venn/p/5494602.html
主要參考文章 2:http://elim.iteye.com/blog/2049110
主要參考文章 3:http://doc.okbase.net/oh_Maxy/archive/154764.html
一、基本屬性
建議大家使用 office word 來創建文檔。(wps 和 word 結構有些不一樣)
IBodyElement ------------------- 迭代器(段落和表格)
XWPFComment ------------------- 評論(個人理解應該是批注)
XWPFSDT
XWPFFooter ------------------- 頁腳
XWPFFootnotes ------------------- 腳注
XWPFHeader ------------------- 頁眉
XWPFHyperlink ------------------- 超鏈接
XWPFNumbering ------------------- 編號(我也不知是啥...)
XWPFParagraph ------------------- 段落
XWPFPictureData ------------------- 圖片
XWPFStyles ------------------- 樣式(設置多級標題的時候用)
XWPFTable ------------------- 表格
二、正文段落
一個文檔包含多個段落,一個段落包含多個 Runs,一個 Runs 包含多個 Run,Run 是文檔的最小單元
獲取所有段落:List paragraphs = word.getParagraphs();
獲取一個段落中的所有 Runs:List xwpfRuns = xwpfParagraph.getRuns();
獲取一個 Runs 中的一個 Run:XWPFRun run = xwpfRuns.get(index);
XWPFRun-- 代表具有相同屬性的一段文本
三、正文表格
一個文檔包含多個表格,一個表格包含多行,一行包含多列(格),每一格的內容相當於一個完整的文檔
獲取所有表格:List xwpfTables = doc.getTables();
獲取一個表格中的所有行:List xwpfTableRows = xwpfTable.getRows();
獲取一行中的所有列:List xwpfTableCells = xwpfTableRow.getTableCells();
獲取一格里的內容:List paragraphs = xwpfTableCell.getParagraphs();
之后和正文段落一樣
注:
- 表格的一格相當於一個完整的 docx 文檔,只是沒有頁眉和頁腳。里面可以有表格,使用 xwpfTableCell.getTables() 獲取,and so on
- 在 poi 文檔中段落和表格是完全分開的,如果在兩個段落中有一個表格,在 poi 中是沒辦法確定表格在段落中間的。(當然除非你本來知道了,這句是廢話)。只有文檔的格式固定,才能正確的得到文檔的結構
個人理解:我不能確定表格所處的位置(第一個段落后面 ,還是第二個段落后面...)
3、頁眉:
一個文檔可以有多個頁眉, 頁眉里面可以包含段落和表格
獲取文檔的頁眉:List headerList = doc.getHeaderList();
獲取頁眉里的所有段落:List paras = header.getParagraphs();
獲取頁眉里的所有表格:List tables = header.getTables();
之后就一樣了
四、頁腳:
頁腳和頁眉基本類似,可以獲取表示頁數的角標
言歸正傳 ------- 干貨:
五、通過 XWPFDocument 讀:段落 + 表格
a、獲取文檔的所有段落
InputStream is = new FileInputStream("D:\table.docx");
XWPFDocument doc = new XWPFDocument(is);
List paras = doc.getParagraphs();
獲取段落內容
for (XWPFParagraph para : paras) {// 當前段落的屬性 //CTPPr pr = para.getCTP().getPPr();
System.out.println(para.getText());
}
b、獲取文檔中所有的表格
List tables = doc.getTables(); List rows; List cells; for (XWPFTable table : tables) { // 表格屬性 CTTblPr pr = table.getCTTbl().getTblPr(); // 獲取表格對應的行 rows = table.getRows(); for (XWPFTableRow row : rows) { // 獲取行對應的單元格 cells = row.getTableCells(); for (XWPFTableCell cell : cells) { System.out.println(cell.getText());; } } }
六、XWPFDocument 生成 word
直接 new 一個空的 XWPFDocument,之后再往這個 XWPFDocument 里面填充內容,然后再把它寫入到對應的輸出流中。
新建一個文檔
XWPFDocument doc = new XWPFDocument(); //創建一個段落 XWPFParagraph para = doc.createParagraph(); //一個XWPFRun代表具有相同屬性的一個區域:一段文本 XWPFRun run = para.createRun(); run.setBold(true); // 加粗 run.setText("加粗的內容"); run = para.createRun(); run.setColor("FF0000"); run.setText("紅色的字。"); OutputStream os = new FileOutputStream("D:\\simpleWrite.docx"); //把doc輸出到輸出流 doc.write(os); this.close(os);
新建一個表格
//XWPFDocument doc = new XWPFDocument(); // 創建一個 5 行 5 列的表格 XWPFTable table = doc.createTable(5, 5); // 這里增加的列原本初始化創建的那 5 行在通過 getTableCells()方法獲取時獲取不到,但通過 row 新增的就可以。 //table.addNewCol(); // 給表格增加一列,變成 6 列 table.createRow(); // 給表格新增一行,變成 6 行 List rows = table.getRows(); // 表格屬性 CTTblPr tablePr = table.getCTTbl().addNewTblPr(); // 表格寬度 CTTblWidth width = tablePr.addNewTblW(); width.setW(BigInteger.valueOf(8000)); XWPFTableRow row; List cells; XWPFTableCell cell; int rowSize = rows.size(); int cellSize; for (int i=0; i) { row = rows.get(i); // 新增單元格 row.addNewTableCell(); // 設置行的高度 row.setHeight(500); // 行屬性 //CTTrPr rowPr = row.getCtRow().addNewTrPr(); // 這種方式是可以獲取到新增的 cell 的。 //List list = row.getCtRow().getTcList(); cells = row.getTableCells(); cellSize = cells.size(); for (int j=0; j) { cell = cells.get(j); if ((i+j)%2==0) { // 設置單元格的顏色 cell.setColor("ff0000"); // 紅色 } else { cell.setColor("0000ff"); // 藍色 } // 單元格屬性 CTTcPr cellPr = cell.getCTTc().addNewTcPr(); cellPr.addNewVAlign().setVal(STVerticalJc.CENTER); if (j == 3) { // 設置寬度 cellPr.addNewTcW().setW(BigInteger.valueOf(3000)); } cell.setText(i + "," + j); } } // 文件不存在時會自動創建 OutputStream os = new FileOutputStream("D:\\table.docx"); // 寫入文件 doc.write(os); this.close(os);
七、段落內容替換
/** * 替換段落里面的變量 * @param para 要替換的段落 * @param params 參數 */ private void replaceInPara(XWPFParagraph para, Map params) { List runs; Matcher matcher; if (this.matcher(para.getParagraphText()).find()) { runs = para.getRuns(); for (int i=0; i) { XWPFRun run = runs.get(i); String runText = run.toString(); matcher = this.matcher(runText); if (matcher.find()) { while ((matcher = this.matcher(runText)).find()) { runText = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1)))); } // 直接調用 XWPFRun 的 setText() 方法設置文本時,在底層會重新創建一個 XWPFRun,把文本附加在當前文本后面, // 所以我們不能直接設值,需要先刪除當前 run, 然后再自己手動插入一個新的 run。 para.removeRun(i); para.insertNewRun(i).setText(runText); } } } }
直接調用 XWPFRun 的 setText() 方法設置文本時,在底層會重新創建一個 XWPFRun,把文本附加在當前文本后面,所以我們不能直接設值,需要先刪除當前 run, 然后再自己手動插入一個新的 run。
// 抽取 word docx 文件中的圖片
String path ="D://abc.docx"; File file = new File(path); try { FileInputStream fis = new FileInputStream(file); XWPFDocument document = new XWPFDocument(fis); XWPFWordExtractor xwpfWordExtractor = new XWPFWordExtractor(document); String text = xwpfWordExtractor.getText(); System.out.println(text); List picList =document.getAllPictures(); for (XWPFPictureData pic : picList) { System.out.println(pic.getPictureType() + file.separator + pic.suggestFileExtension() +file.separator+pic.getFileName()); byte[] bytev = pic.getData(); FileOutputStream fos = new FileOutputStream("D:\\abc\\docxImage\\"+pic.getFileName()); fos.write(bytev); } fis.close(); } catch (IOException e) { e.printStackTrace(); } }
八、多級標題結構
/** * 自定義樣式方式寫 word,參考 statckoverflow 的源碼 * * @throws IOException */ public static void writeSimpleDocxFile() throws IOException { XWPFDocument docxDocument = new XWPFDocument(); // 老外自定義了一個名字,中文版的最好還是按照 word 給的標題名來,否則級別上可能會亂 addCustomHeadingStyle(docxDocument, " 標題 1", 1); addCustomHeadingStyle(docxDocument, " 標題 2", 2); // 標題 1 XWPFParagraph paragraph = docxDocument.createParagraph(); XWPFRun run = paragraph.createRun(); run.setText(" 標題 1"); paragraph.setStyle(" 標題 1"); // 標題 2 XWPFParagraph paragraph2 = docxDocument.createParagraph(); XWPFRun run2 = paragraph2.createRun(); run2.setText(" 標題 2"); paragraph2.setStyle(" 標題 2"); // 正文 XWPFParagraph paragraphX = docxDocument.createParagraph(); XWPFRun runX = paragraphX.createRun(); runX.setText("正文"); // word 寫入到文件 FileOutputStream fos = new FileOutputStream("D:/myDoc2.docx"); docxDocument.write(fos); fos.close();} /** * 增加自定義標題樣式。這里用的是stackoverflow的源碼 * * @param docxDocument 目標文檔 * @param strStyleId 樣式名稱 * @param headingLevel 樣式級別 */ private static void addCustomHeadingStyle(XWPFDocument docxDocument, String strStyleId, int headingLevel) { CTStyle ctStyle = CTStyle.Factory.newInstance(); ctStyle.setStyleId(strStyleId); CTString styleName = CTString.Factory.newInstance(); styleName.setVal(strStyleId); ctStyle.setName(styleName); CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance(); indentNumber.setVal(BigInteger.valueOf(headingLevel)); // lower number > style is more prominent in the formats bar ctStyle.setUiPriority(indentNumber); CTOnOff onoffnull = CTOnOff.Factory.newInstance(); ctStyle.setUnhideWhenUsed(onoffnull); // style shows up in the formats bar ctStyle.setQFormat(onoffnull); // style defines a heading of the given level CTPPr ppr = CTPPr.Factory.newInstance(); ppr.setOutlineLvl(indentNumber); ctStyle.setPPr(ppr); XWPFStyle style = new XWPFStyle(ctStyle); // is a null op if already defined XWPFStyles styles = docxDocument.createStyles(); style.setType(STStyleType.PARAGRAPH); styles.addStyle(style); }
創建文本對象
XWPFDocument docxDocument = new XWPFDocument();
創建段落對象
XWPFParagraph paragraphX = docxDocument.createParagraph();
XWPFParagraph 段落屬性
//paragraphX.addRun(runX0);//似乎並沒有什么卵用 //paragraphX.removeRun(1);//按數組下標刪除run(文本) paragraphX.setAlignment(ParagraphAlignment.LEFT);//對齊方式 //paragraphX.setBorderBetween(Borders.LIGHTNING_1);//邊界 (但是我設置了好幾個值都沒有效果) //paragraphX.setFirstLineIndent(100);//首行縮進:-----效果不詳 //paragraphX.setFontAlignment(3);//字體對齊方式:1左對齊 2居中3右對齊 //paragraphX.setIndentationFirstLine(567);//首行縮進:567==1厘米 //paragraphX.setIndentationHanging(567);//指定縮進,從父段落的第一行刪除,將第一行上的縮進移回到文本流方向的開頭。 //paragraphX.setIndentationLeft(2);//-----效果不詳 //paragraphX.setIndentationRight(2);//-----效果不詳 //paragraphX.setIndentFromLeft(2);//-----效果不詳 //paragraphX.setIndentFromRight(2);//-----效果不詳 //paragraphX.setNumID(new BigInteger("3"));//設置段落編號-----有效果看不懂(僅僅是整段縮進4個字) //paragraphX.setPageBreak(true);//段前分頁 //paragraphX.setSpacingAfter(1);//指定文檔中此段最后一行以絕對單位添加的間距。-----效果不詳 //paragraphX.setSpacingBeforeLines(2);//指定在該行的第一行中添加行單位之前的間距-----效果不詳 //paragraphX.setStyle("標題 3");//段落樣式:需要結合addCustomHeadingStyle(docxDocument, "標題 3", 3)配合使用 paragraphX.setVerticalAlignment(TextAlignment.BOTTOM);//文本對齊方式(我猜在 table 里面會有比較明顯得到效果) paragraphX.setWordWrapped(true);//這個元素指定一個消費者是否應該突破拉丁語文本超過一行的文本范圍,打破單詞跨兩行(打破字符水平)或移動到以下行字(打破字級)-----(我沒看懂: 填個 false 還報異常了)