POI操作word2010實現多級標題結構

jopen 9年前發布 | 116K 次閱讀 POI Office文檔處理

一、 問題背景:
項目中會生成word的報告,但是直接io流寫的報告都是“正文”,沒有生成標題,也就沒法在大綱結構中方便的查看章節內容了。搜了很多資料也請教了一些同事,終于把這個目錄結構的問題搞定了,在此和大家分享一下。

目前我們用的是office2010,因為word2010(2010版本word結構和2007差不多,應該也適用于2007)與 word2003的巨大差異,本方法可能不適用過低版本(主要是裝卸office太耗時了),有低版本office的同學可以試試這個方法是否可行。


二、 準備工作:

  1. JDK1.6或以上(1.8也試過是可以的,1.5的沒試過了)。

    2. 用的是maven管理配置,pom文件引入:

        <dependency>  
    
     <groupId>org.apache.poi</groupId>  
     <artifactId>poi-excelant</artifactId>  
     <version>3.9</version>  
    
    </dependency> </pre>
    三、 具體實現
  2. 構建模板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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!