POI操作word2010實現多級標題結構
一、 問題背景:
項目中會生成word的報告,但是直接io流寫的報告都是“正文”,沒有生成標題,也就沒法在大綱結構中方便的查看章節內容了。搜了很多資料也請教了一些同事,終于把這個目錄結構的問題搞定了,在此和大家分享一下。
目前我們用的是office2010,因為word2010(2010版本word結構和2007差不多,應該也適用于2007)與 word2003的巨大差異,本方法可能不適用過低版本(主要是裝卸office太耗時了),有低版本office的同學可以試試這個方法是否可行。
二、 準備工作:
- JDK1.6或以上(1.8也試過是可以的,1.5的沒試過了)。
2. 用的是maven管理配置,pom文件引入:
<dependency>
</dependency> </pre><groupId>org.apache.poi</groupId> <artifactId>poi-excelant</artifactId> <version>3.9</version>
三、 具體實現 構建模板word。
創建一個word文檔,例如D:/format.docx 。打開此文檔,隨便輸入一行字符,例如輸入: a 。 然后選中這一行,將其設置為“標題1”(或者大綱視圖下的“1級”大綱)。保存,關閉format.docx。2. 代碼實現解析format.docx并新建word文檔,寫入標題到新建文檔中。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles;public class WordTitle {
/** * word整體樣式 */ private static CTStyles wordStyles = null; /** * Word整體樣式 */ static { XWPFDocument template; try { // 讀取模板文檔 template = new XWPFDocument(new FileInputStream("D:/format.docx")); // 獲得模板文檔的整體樣式 wordStyles = template.getStyle(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (XmlException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { // 新建的word文檔對象 XWPFDocument doc = new XWPFDocument(); // 獲取新建文檔對象的樣式 XWPFStyles newStyles = doc.createStyles(); // 關鍵行// 修改設置文檔樣式為靜態塊中讀取到的樣式 newStyles.setStyles(wordStyles); // 開始內容輸入 // 標題1,1級大綱 XWPFParagraph para1 = doc.createParagraph(); // 關鍵行// 1級大綱 para1.setStyle("1"); XWPFRun run1 = para1.createRun(); // 標題內容 run1.setText("標題 1"); // 標題2 XWPFParagraph para2 = doc.createParagraph(); // 關鍵行// 2級大綱 para2.setStyle("2"); XWPFRun run2 = para2.createRun(); // 標題內容 run2.setText("標題 2"); // 正文 XWPFParagraph paraX = doc.createParagraph(); XWPFRun runX = paraX.createRun(); // 正文內容 runX.setText("正文"); // word寫入到文件 FileOutputStream fos = new FileOutputStream("D:/myDoc.docx"); doc.write(fos); fos.close(); }
} </pre>
四、 特別說明1. format.docx中只有標題1,雖然代碼中設置了標題2(2級大綱),但是沒有生效,“標題2”仍然是正文格式。如果想生效,需要在format.docx中新增一行,并設置為標題2(2級大綱)。
2. 一個奇怪的現象:設置完標題2,解析一次之后,再次將標題2設置為正文格式,或者刪除,讀取的wordStyles格式仍然能夠設置標題2。
3. 大家可以打印下wordStyles,看看“標題1”附近的一些參數值,有助于理解和記憶。
五、 參考資料
參考了stackoverflow一篇關于POI的問題 。
最終解決問題,是這個鏈接給了靈感。老外用的是英文版的word,最終實現上還是和中文版的略有不同。
此外,大家往下看會發現一段代碼,沒有依賴模板,直接正向實現的。這種方式的實現,需要自己調整標題的字體大小、是否加粗等,個人感覺挺繁瑣的。
具體實現如下(注意包的引入):
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyle;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTString;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyle;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STStyleType;public class WordTitle {
/** * word整體樣式 */ private static CTStyles wordStyles = null; /** * Word整體樣式 */ static { XWPFDocument template; try { // 讀取模板文檔 template = new XWPFDocument(new FileInputStream("D:/format.docx")); // 獲得模板文檔的整體樣式 wordStyles = template.getStyle(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (XmlException e) { e.printStackTrace(); } } // 模板方式實現 public static void formatDoc() throws IOException { // 新建的word文檔對象 XWPFDocument doc = new XWPFDocument(); // 獲取新建文檔對象的樣式 XWPFStyles newStyles = doc.createStyles(); // 關鍵行// 修改設置文檔樣式為靜態塊中讀取到的樣式 newStyles.setStyles(wordStyles); // 開始內容輸入 // 標題1,1級大綱 XWPFParagraph para1 = doc.createParagraph(); // 關鍵行// 1級大綱 para1.setStyle("1"); XWPFRun run1 = para1.createRun(); // 標題內容 run1.setText("標題 1"); // 標題2 XWPFParagraph para2 = doc.createParagraph(); // 關鍵行// 2級大綱 para2.setStyle("2"); XWPFRun run2 = para2.createRun(); // 標題內容 run2.setText("標題 2"); // 正文 XWPFParagraph paraX = doc.createParagraph(); XWPFRun runX = paraX.createRun(); // 正文內容 runX.setText("正文"); // word寫入到文件 FileOutputStream fos = new FileOutputStream("D:/myDoc.docx"); doc.write(fos); fos.close(); } // main public static void main(String[] args) throws Exception { // 讀取模板方式寫word formatDoc(); // 自定義樣式方式寫word writeSimpleDocxFile(); } /** * 自定義樣式方式寫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); }
} </pre>
來自:http://blog.csdn.net/oh_maxy/article/details/46515619