向Docx4j生成的word文档中添加布局--第二部分

原文标题:Adding layout to your Docx4j-generated word documents, part 2

原文链接:http://blog.iprofs.nl/2012/11/19/adding-layout-to-your-docx4j-generated-word-documents-part-2/

原文作者:lvdpal

发表日期:2012年11月19日

注:我没有再试着翻译代码中的注释,因为我觉得我水平实在有限,翻译过来的注释还不如看英文来地明白...


 

在前面发表的两篇博客中,我写了一些关于在docx文档中创建表格添加图像和布局的内容。本篇博客中我将继续介绍一些文档布局相关的示例:

 

更改默认样式

 

几乎所有的客户都想要他们自己的风格。Word提供了一些默认的样式但它们不够好(注意一下我没有更改两个图像之间的文本,因此图像中的文本并不反映实际的风格):

大多数客户会想改变这些风格,在应用了我们在本例中创建的风格之后,上面的word文档看起来会像这样:

 

[java] view plain copy
 
  1. public class ChangingTheStyleSheet {  
  2.     private static WordprocessingMLPackage  wordMLPackage;  
  3.    
  4.     /** 
  5.      *  First we create the package, then we alter the style sheet and add some 
  6.      *  styled paragraphs. Finally we save the package. 
  7.      */  
  8.     public static void main (String[] args) throws Docx4JException {  
  9.         wordMLPackage = WordprocessingMLPackage.createPackage();  
  10.         alterStyleSheet();  
  11.    
  12.         wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title",  
  13.             "Hello World! This title is now in Arial.");  
  14.         wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle",  
  15.             "Subtitle, this subtitle is now Arial too");  
  16.         wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Heading1",  
  17.             "As is Heading1");  
  18.         wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Heading2",  
  19.             "Heading2 is now Arial, no longer bold and has an underline " +  
  20.             "and fontsize 12");  
  21.         wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Heading3",  
  22.             "Heading3 is now Arial");  
  23.         wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Normal",  
  24.             "And normal text has changed to Arial and fontsize 10");  
  25.    
  26.         wordMLPackage.save(new java.io.File("src/main/files/HelloWord12.docx") );  
  27.     }  
  28.    
  29.     /** 
  30.      *  This method alters the default style sheet that is part of each document. 
  31.      * 
  32.      *  To do this, we first retrieve the style sheet from the package and then 
  33.      *  get the Styles object from it. From this object, we get the list of actual 
  34.      *  styles and iterate over them. 
  35.      *  We check against all styles we want to alter and apply the alterations if 
  36.      *  applicable. 
  37.      * 
  38.      *  @param wordMLPackage 
  39.      */  
  40.     public static void alterStyleSheet() {  
  41.         StyleDefinitionsPart styleDefinitionsPart =  
  42.             wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart();  
  43.         Styles styles = styleDefinitionsPart.getJaxbElement();  
  44.    
  45.         List<Style>  stylesList = styles.getStyle();  
  46.         for (Style style : stylesList) {  
  47.             if (style.getStyleId().equals("Normal")) {  
  48.                 alterNormalStyle(style);  
  49.             } else if (style.getStyleId().equals("Heading2")) {  
  50.                 alterHeading2Style(style);  
  51.             } else if (style.getStyleId().equals("Heading1") ||  
  52.                     style.getStyleId().equals("Heading3") ||  
  53.                     style.getStyleId().equals("Title") ||  
  54.                     style.getStyleId().equals("Subtitle")) {  
  55.                 getRunPropertiesAndRemoveThemeInfo(style);  
  56.             }  
  57.         }  
  58.     }  
  59.    
  60.     /** 
  61.      *  First we create a run properties object as we want to remove nearly all of 
  62.      *  the existing styling. Then we change the font and font size and set the 
  63.      *  run properties on the given style. As in previous examples, the font size 
  64.      *  is defined to be in half-point size. 
  65.      */  
  66.     private static void alterNormalStyle(Style style) {  
  67.         // we want to change (or remove) almost all the run properties of the  
  68.         // normal style, so we create a new one.  
  69.         RPr rpr = new RPr();  
  70.         changeFontToArial(rpr);  
  71.         changeFontSize(rpr, 20);  
  72.         style.setRPr(rpr);  
  73.     }  
  74.    
  75.     /** 
  76.      *  For this style, we get the existing run properties from the style and 
  77.      *  remove the theme font information from them. Then we also remove the bold 
  78.      *  styling, change the font size (half-points) and add an underline. 
  79.      */  
  80.     private static void alterHeading2Style(Style style) {  
  81.         RPr rpr = getRunPropertiesAndRemoveThemeInfo(style);  
  82.         removeBoldStyle(rpr);  
  83.         changeFontSize(rpr, 24);  
  84.         addUnderline(rpr);  
  85.     }  
  86.    
  87.     private static RPr getRunPropertiesAndRemoveThemeInfo(Style style) {  
  88.         // We only want to change some settings, so we get the existing run  
  89.         // properties from the style.  
  90.         RPr rpr = style.getRPr();  
  91.         removeThemeFontInformation(rpr);  
  92.         return rpr;  
  93.     }  
  94.    
  95.     /** 
  96.      *  Change the font of the given run properties to Arial. 
  97.      * 
  98.      *  A run font specifies the fonts which shall be used to display the contents 
  99.      *  of the run. Of the four possible types of content, we change the styling of 
  100.      *  two of them: ASCII and High ANSI. 
  101.      *  Finally we add the run font to the run properties. 
  102.      * 
  103.      *  @param runProperties 
  104.      */  
  105.     private static void changeFontToArial(RPr runProperties) {  
  106.         RFonts runFont = new RFonts();  
  107.         runFont.setAscii("Arial");  
  108.         runFont.setHAnsi("Arial");  
  109.         runProperties.setRFonts(runFont);  
  110.     }  
  111.    
  112.     /** 
  113.      * Change the font size of the given run properties to the given value. 
  114.      * 
  115.      * @param runProperties 
  116.      * @param fontSize  Twice the size needed, as it is specified as half-point value 
  117.      */  
  118.     private static void changeFontSize(RPr runProperties, int fontSize) {  
  119.         HpsMeasure size = new HpsMeasure();  
  120.         size.setVal(BigInteger.valueOf(fontSize));  
  121.         runProperties.setSz(size);  
  122.     }  
  123.    
  124.     /** 
  125.      * Removes the theme font information from the run properties. 
  126.      * If this is not removed then the styles based on the normal style won't 
  127.      * inherit the Arial font from the normal style. 
  128.      * 
  129.      * @param runProperties 
  130.      */  
  131.     private static void removeThemeFontInformation(RPr runProperties) {  
  132.         runProperties.getRFonts().setAsciiTheme(null);  
  133.         runProperties.getRFonts().setHAnsiTheme(null);  
  134.     }  
  135.    
  136.     /** 
  137.      * Removes the Bold styling from the run properties. 
  138.      * 
  139.      * @param runProperties 
  140.      */  
  141.     private static void removeBoldStyle(RPr runProperties) {  
  142.         runProperties.getB().setVal(false);  
  143.     }  
  144.    
  145.     /** 
  146.      * Adds a single underline to the run properties. 
  147.      * 
  148.      * @param runProperties 
  149.      */  
  150.     private static void addUnderline(RPr runProperties) {  
  151.         U underline = new U();  
  152.         underline.setVal(UnderlineEnumeration.SINGLE);  
  153.         runProperties.setU(underline );  
  154.     }  
  155. }  

 

添加页脚或页眉

 

 

添加页脚或页眉(或二者兼具)是一个很多客户都想要的特性。幸运的是,做这个并不是很难。这个例子是docx4j示例的一部分,但是我删掉了添加图像的部分只展示最基本的添加页脚部分。添加页眉,你需要做的仅仅是用header来替换footer(或者hdr替换ftr)。

 

[java] view plain copy
 
  1. public class AddingAFooter {  
  2.     private static WordprocessingMLPackage wordMLPackage;  
  3.     private static ObjectFactory factory;  
  4.    
  5.     /** 
  6.      *  First we create the package and the factory. Then we create the footer part, 
  7.      *  which returns a relationship. This relationship is then used to create 
  8.      *  a reference. Finally we add some text to the document and save it. 
  9.      */  
  10.     public static void main (String[] args) throws Docx4JException {  
  11.         wordMLPackage = WordprocessingMLPackage.createPackage();  
  12.         factory = Context.getWmlObjectFactory();  
  13.    
  14.         Relationship relationship = createFooterPart();  
  15.         createFooterReference(relationship);  
  16.    
  17.         wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");  
  18.    
  19.         wordMLPackage.save(new File("src/main/files/HelloWord14.docx") );  
  20.     }  
  21.    
  22.     /** 
  23.      *  This method creates a footer part and set the package on it. Then we add some 
  24.      *  text and add the footer part to the package. Finally we return the 
  25.      *  corresponding relationship. 
  26.      * 
  27.      *  @return 
  28.      *  @throws InvalidFormatException 
  29.      */  
  30.     private static Relationship createFooterPart() throws InvalidFormatException {  
  31.         FooterPart footerPart = new FooterPart();  
  32.         footerPart.setPackage(wordMLPackage);  
  33.    
  34.         footerPart.setJaxbElement(createFooter("Text"));  
  35.    
  36.         return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);  
  37.     }  
  38.    
  39.     /** 
  40.      *  First we create a footer, a paragraph, a run and a text. We add the given 
  41.      *  given content to the text and add that to the run. The run is then added to 
  42.      *  the paragraph, which is in turn added to the footer. Finally we return the 
  43.      *  footer. 
  44.      * 
  45.      *  @param content 
  46.      *  @return 
  47.      */  
  48.     private static Ftr createFooter(String content) {  
  49.         Ftr footer = factory.createFtr();  
  50.         P paragraph = factory.createP();  
  51.         R run = factory.createR();  
  52.         Text text = new Text();  
  53.         text.setValue(content);  
  54.         run.getContent().add(text);  
  55.         paragraph.getContent().add(run);  
  56.         footer.getContent().add(paragraph);  
  57.         return footer;  
  58.     }  
  59.    
  60.     /** 
  61.      *  First we retrieve the document sections from the package. As we want to add 
  62.      *  a footer, we get the last section and take the section properties from it. 
  63.      *  The section is always present, but it might not have properties, so we check 
  64.      *  if they exist to see if we should create them. If they need to be created, 
  65.      *  we do and add them to the main document part and the section. 
  66.      *  Then we create a reference to the footer, give it the id of the relationship, 
  67.      *  set the type to header/footer reference and add it to the collection of 
  68.      *  references to headers and footers in the section properties. 
  69.      * 
  70.      * @param relationship 
  71.      */  
  72.     private static void createFooterReference(Relationship relationship) {  
  73.         List<SectionWrapper> sections =  
  74.             wordMLPackage.getDocumentModel().getSections();  
  75.    
  76.         SectPr sectionProperties = sections.get(sections.size() - 1).getSectPr();  
  77.         // There is always a section wrapper, but it might not contain a sectPr  
  78.         if (sectionProperties==null ) {  
  79.             sectionProperties = factory.createSectPr();  
  80.             wordMLPackage.getMainDocumentPart().addObject(sectionProperties);  
  81.             sections.get(sections.size() - 1).setSectPr(sectionProperties);  
  82.         }  
  83.    
  84.         FooterReference footerReference = factory.createFooterReference();  
  85.         footerReference.setId(relationship.getId());  
  86.         footerReference.setType(HdrFtrRef.DEFAULT);  
  87.         sectionProperties.getEGHdrFtrReferences().add(footerReference);  
  88.     }  
  89. }  

 

在页脚添加页码

 

我在本博客要讨论的最后一个特性是怎样在我们刚创建的页脚处添加页码。你可能已经知道,在Word中页码是一个域,因此本例或多或少是前面的两个例子的结合:目录表和页脚。

 

[java] view plain copy
 
  1. public class AddingPageNrToFooter {  
  2.     private static WordprocessingMLPackage wordMLPackage;  
  3.     private static ObjectFactory factory;  
  4.    
  5.     /** 
  6.      *  First we create the package and the factory. Then we create the footer. 
  7.      *  Finally we add two pages with text to the document and save it. 
  8.      */  
  9.     public static void main (String[] args) throws Exception {  
  10.         wordMLPackage = WordprocessingMLPackage.createPackage();  
  11.         factory = Context.getWmlObjectFactory();  
  12.    
  13.         Relationship relationship = createFooterPart();  
  14.         createFooterReference(relationship);  
  15.    
  16.         MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();  
  17.    
  18.         documentPart.addParagraphOfText("Hello World!");  
  19.    
  20.         addPageBreak(documentPart);  
  21.    
  22.         documentPart.addParagraphOfText("This is page 2!");  
  23.         wordMLPackage.save(new File("src/main/files/HelloWord15.docx") );  
  24.     }  
  25.    
  26.     /** 
  27.      *  As in the previous example, this method creates a footer part and adds it to 
  28.      *  the main document and then returns the corresponding relationship. 
  29.      * 
  30.      *  @return 
  31.      *  @throws InvalidFormatException 
  32.      */  
  33.     private static Relationship createFooterPart() throws InvalidFormatException {  
  34.         FooterPart footerPart = new FooterPart();  
  35.         footerPart.setPackage(wordMLPackage);  
  36.    
  37.         footerPart.setJaxbElement(createFooterWithPageNr());  
  38.    
  39.         return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);  
  40.     }  
  41.    
  42.     /** 
  43.      *  As in the previous example, we create a footer and a paragraph object. But 
  44.      *  this time, instead of adding text to a run, we add a field. And just as with 
  45.      *  the table of content, we have to add a begin and end character around the 
  46.      *  actual field with the page number. Finally we add the paragraph to the 
  47.      *  content of the footer and then return it. 
  48.      * 
  49.      * @return 
  50.      */  
  51.     public static Ftr createFooterWithPageNr() {  
  52.         Ftr ftr = factory.createFtr();  
  53.         P paragraph = factory.createP();  
  54.    
  55.         addFieldBegin(paragraph);  
  56.         addPageNumberField(paragraph);  
  57.         addFieldEnd(paragraph);  
  58.    
  59.         ftr.getContent().add(paragraph);  
  60.         return ftr;  
  61.     }  
  62.    
  63.     /** 
  64.      *  Creating the page number field is nearly the same as creating the field in 
  65.      *  the TOC example. The only difference is in the value. We use the PAGE 
  66.      *  command, which prints the number of the current page, together with the 
  67.      *  MERGEFORMAT switch, which indicates that the current formatting should be 
  68.      *  preserved when the field is updated. 
  69.      * 
  70.      * @param paragraph 
  71.      */  
  72.     private static void addPageNumberField(P paragraph) {  
  73.         R run = factory.createR();  
  74.         Text txt = new Text();  
  75.         txt.setSpace("preserve");  
  76.         txt.setValue(" PAGE   \\* MERGEFORMAT ");  
  77.         run.getContent().add(factory.createRInstrText(txt));  
  78.         paragraph.getContent().add(run);  
  79.     }  
  80.    
  81.     /** 
  82.      * Every fields needs to be delimited by complex field characters. This method 
  83.      * adds the delimiter that precedes the actual field to the given paragraph. 
  84.      * @param paragraph 
  85.      */  
  86.     private static void addFieldBegin(P paragraph) {  
  87.         R run = factory.createR();  
  88.         FldChar fldchar = factory.createFldChar();  
  89.         fldchar.setFldCharType(STFldCharType.BEGIN);  
  90.         run.getContent().add(fldchar);  
  91.         paragraph.getContent().add(run);  
  92.     }  
  93.    
  94.     /** 
  95.      * Every fields needs to be delimited by complex field characters. This method 
  96.      * adds the delimiter that follows the actual field to the given paragraph. 
  97.      * @param paragraph 
  98.      */  
  99.     private static void addFieldEnd(P paragraph) {  
  100.         FldChar fldcharend = factory.createFldChar();  
  101.         fldcharend.setFldCharType(STFldCharType.END);  
  102.         R run3 = factory.createR();  
  103.         run3.getContent().add(fldcharend);  
  104.         paragraph.getContent().add(run3);  
  105.     }  
  106.    
  107.     /** 
  108.      * This method fetches the document final section properties, and adds a newly 
  109.      * created footer reference to them. 
  110.      * 
  111.      * @param relationship 
  112.      */  
  113.     public static void createFooterReference(Relationship relationship){  
  114.    
  115.         List<SectionWrapper> sections =  
  116.             wordMLPackage.getDocumentModel().getSections();  
  117.    
  118.         SectPr sectPr = sections.get(sections.size() - 1).getSectPr();  
  119.         // There is always a section wrapper, but it might not contain a sectPr  
  120.         if (sectPr==null ) {  
  121.             sectPr = factory.createSectPr();  
  122.             wordMLPackage.getMainDocumentPart().addObject(sectPr);  
  123.             sections.get(sections.size() - 1).setSectPr(sectPr);  
  124.         }  
  125.    
  126.         FooterReference footerReference = factory.createFooterReference();  
  127.         footerReference.setId(relationship.getId());  
  128.         footerReference.setType(HdrFtrRef.DEFAULT);  
  129.         sectPr.getEGHdrFtrReferences().add(footerReference);  
  130.     }  
  131.    
  132.     /** 
  133.      * Adds a page break to the document. 
  134.      * 
  135.      * @param documentPart 
  136.      */  
  137.     private static void addPageBreak(MainDocumentPart documentPart) {  
  138.         Br breakObj = new Br();  
  139.         breakObj.setType(STBrType.PAGE);  
  140.    
  141.         P paragraph = factory.createP();  
  142.         paragraph.getContent().add(breakObj);  
  143.         documentPart.getJaxbElement().getBody().getContent().add(paragraph);  
  144.     }  
  145. }  

 

总结

 

在三篇博客中,我展示了怎样创建一个word文档以及一些很常用的特性。这些示例全都是我的客户要求我需要创建的word文档。而且我觉得它们足以让你知道如何去做更多其它的事情,如果不是,用你所需要的特性创建一个文档并去查看内部的XML,如果一切仍然不能奏效,你可以到Docx4j论坛进行提问,docx4j的作者会定期尝试回答所有的问题。

在以后的博客中我会展示一些关于xlsx文档的示例。

posted @ 2017-11-26 16:17  chenxiangxiang  阅读(1271)  评论(0编辑  收藏  举报