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

时间:2023-02-18 11:16:30

原文标题: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提供了一些默认的样式但它们不够好(注意一下我没有更改两个图像之间的文本,因此图像中的文本并不反映实际的风格):

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

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

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

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

添加页脚或页眉

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

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

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

在页脚添加页码

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

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

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

总结

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

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

向Docx4j生成的word文档中添加布局--第二部分的更多相关文章

  1. 向Docx4j生成的word文档添加图片和布局--第一部分

    原文标题:Adding images and layout to your Docx4j-generated word documents, part 1 原文链接:http://blog.iprof ...

  2. 如何在word文档中添加mathtype加载项

    MathType是强大的数学公式编辑器,通常与office一起使用,mathtype安装完成后,正常情况下会在word文档中的菜单中自动添加mathtype加载项,但有时也会出现小意外,mathtyp ...

  3. 在word文档中添加上角标和下角标

    方法一 摘录:https://jingyan.baidu.com/article/02027811b4d2da1bcc9ce5f7.html 方法二 利用MathType数学公式编辑器 exe下载:h ...

  4. C&num; 在Word文档中生成条形码

    C# 在Word文档中生成条形码 简介 条形码是由多个不同的空白和黑条按照一定的顺序组成,用于表示各种信息如产品名称.制造商.类别.价格等.目前,条形码在我们的日常生活中有着很广泛的应用,不管是在图书 ...

  5. C&num; 在word文档中复制表格并粘帖到下一页中

    C# 在word文档中复制表格并粘帖到下一页中 object oMissing = System.Reflection.Missing.Value;            Microsoft.Offi ...

  6. C&num; Word文档中插入、提取图片,文字替换图片

    Download Files:ImageOperationsInWord.zip 简介 在这篇文章中我们可以学到在C#程序中使用一个Word文档对图像的各种操作.图像会比阅读文字更有吸引力,而且图像是 ...

  7. Android开发——使用Jword生成本地word文档

    本文主要介绍如何使用Jword生成本地word文档,这里涉及到Jword的使用技巧,本文给出相应的代码,需要的朋友可以参考下. 为什么使用Jword呢?因为IText .Freemark在安卓平台上压 ...

  8. java生成复杂word文档

    在Web应用中,有时需要按照固定的模板将数据导出到Word,如流程审批单,在流程处理完成后将处理过程按照流程单的要求导出,有时程序中需要实现生成 标准Word文档,要求能够打印,并且保持页面样式不变, ...

  9. 利用POI操作不同版本号word文档中的图片以及创建word文档

    我们都知道要想利用java对office操作最经常使用的技术就应该是POI了,在这里本人就不多说到底POI是什么和怎么用了. 先说本人遇到的问题,不同于利用POI去向word文档以及excel文档去写 ...

随机推荐

  1. ios app的版本号

    ios其实有3个版本号 version 就是ios的版本号 (只能分3段,并且都是数字) build 是ios构建内部版本时的版本号 (可以分4段) 而提交到appstore时, 还是要创建一个sku ...

  2. (翻译)《Hands-on Node&period;js》—— Introduction

    今天开始会和大熊君{{bb}}一起着手翻译node的系列外文书籍,大熊负责翻译<Node.js IN ACTION>一书,而我暂时负责翻译这本<Hands-on Node.js&gt ...

  3. C&plus;&plus;混合编程之idlcpp教程Python篇&lpar;3&rpar;

    上一篇 C++混合编程之idlcpp教程Python篇(2) 是一个 hello world 的例子,仅仅涉及了静态函数的调用.这一篇会有新的内容. 与PythonTutorial0相似,工程Pyth ...

  4. &ast;&ast;&ast;CI中的数据库操作&lpar;insert&lowbar;id新增后返回记录ID&rpar;

    在system/application/config 文件夹和里面的config文件里已经配置了参数 $active_group = "default";$db['default' ...

  5. 夺命雷公狗ThinkPHP项目之----企业网站6之栏目的添加(主要用模型来验证字段)

    我们刚才的控制器已经写好了,那么我们现在就来完成我们的模型, 首先我们在Model目录下创建一个CategoryModel.class.php 代码如下: <?php namespace Adm ...

  6. leetcode:Integer to Roman(整数转化为罗马数字)

    Question: Given an integer, convert it to a roman numeral. Input is guaranteed to be within the rang ...

  7. win32 sdk树形控件的项拖拽实现

    本课中,我们将学习如何使用树型视图控件.另外还要学习如何在树型视图中完成拖-拉动作,以及如何使用图象列表. 理论: 树型视图是一种特别的窗口,我们可以使用它一目了然地表示某种层次关系.譬如象在资源管理 ...

  8. Python之CVXOPT模块

      Python中支持Convex Optimization(凸规划)的模块为CVXOPT,其安装方式为: 卸载原Pyhon中的Numpy 安装CVXOPT的whl文件,链接为:https://www ...

  9. 测试系统工程师TSE的职责与培养

    测试系统工程师TSE的职责与培养 研发资深顾问 杨学明 如今,国内所有的研发型的公司都有测试部门,无论测试团队大小,都有测试组长,测试经理,测试工程师等头衔,但随着产品和业务的质量要求越来越高,产品的 ...

  10. 1、git基础介绍及远程&sol;本地仓库、分支

    1. Git基础介绍 基于Git进行开发时,首先需要将远程仓库代码clone到本地,即为本地仓库.后续大部分时间都是基于本地仓库上的分支进行编码,最后将本地仓库的代码合入远程仓库. 1.1. 远程仓库 ...