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

原文链接:http://blog.iprofs.nl/2012/10/22/adding-images-and-layout-to-your-docx4j-generated-word-documents-part-1/

原文作者:lvdpal

发表日期:2012年10月22日

注:由于我对docx4j也不是很熟悉,所以很多专业名词不会翻译,如果文章内容使您感到困惑,请移步到原文。


前一篇博客,我介绍了如何在docx文档中生成表格。这篇博客中我会展示一些关于图片、分页符和目录表的示例:

添加图片

本示例我们向新创建的word文档添加一张嵌入式图片。这并非很难,因为Docx4j示例包含了几种做这件事的方法。但是有几个问题我不是特别地清楚,所以我稍微重构了那个例子改为只添加一个内联图片并且添加了一些注释来解释发生了什么。

  1. public class AddingAnInlineImage {
  2. /**
  3. *  像往常一样, 我们创建了一个包(package)来容纳文档.
  4. *  然后我们创建了一个指向将要添加到文档的图片的文件对象.为了能够对图片做一些操作, 我们将它转换
  5. *  为字节数组. 最后我们将图片添加到包中并保存这个包(package).
  6. */
  7. public static void main (String[] args) throws Exception {
  8. WordprocessingMLPackage  wordMLPackage = WordprocessingMLPackage.createPackage();
  9. File file = new File("src/main/resources/iProfsLogo.png");
  10. byte[] bytes = convertImageToByteArray(file);
  11. addImageToPackage(wordMLPackage, bytes);
  12. wordMLPackage.save(new java.io.File("src/main/files/HelloWord7.docx"));
  13. }
  14. /**
  15. *  Docx4j拥有一个由字节数组创建图片部件的工具方法, 随后将其添加到给定的包中. 为了能将图片添加
  16. *  到一个段落中, 我们需要将图片转换成内联对象. 这也有一个方法, 方法需要文件名提示, 替换文本,
  17. *  两个id标识符和一个是嵌入还是链接到的指示作为参数.
  18. *  一个id用于文档中绘图对象不可见的属性, 另一个id用于图片本身不可见的绘制属性. 最后我们将内联
  19. *  对象添加到段落中并将段落添加到包的主文档部件.
  20. *
  21. *  @param wordMLPackage 要添加图片的包
  22. *  @param bytes         图片对应的字节数组
  23. *  @throws Exception    不幸的createImageInline方法抛出一个异常(没有更多具体的异常类型)
  24. */
  25. private static void addImageToPackage(WordprocessingMLPackage wordMLPackage,
  26. byte[] bytes) throws Exception {
  27. BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
  28. int docPrId = 1;
  29. int cNvPrId = 2;
  30. Inline inline = imagePart.createImageInline("Filename hint","Alternative text", docPrId, cNvPrId, false);
  31. P paragraph = addInlineImageToParagraph(inline);
  32. wordMLPackage.getMainDocumentPart().addObject(paragraph);
  33. }
  34. /**
  35. *  创建一个对象工厂并用它创建一个段落和一个可运行块R.
  36. *  然后将可运行块添加到段落中. 接下来创建一个图画并将其添加到可运行块R中. 最后我们将内联
  37. *  对象添加到图画中并返回段落对象.
  38. *
  39. * @param   inline 包含图片的内联对象.
  40. * @return  包含图片的段落
  41. */
  42. private static P addInlineImageToParagraph(Inline inline) {
  43. // 添加内联对象到一个段落中
  44. ObjectFactory factory = new ObjectFactory();
  45. P paragraph = factory.createP();
  46. R run = factory.createR();
  47. paragraph.getContent().add(run);
  48. Drawing drawing = factory.createDrawing();
  49. run.getContent().add(drawing);
  50. drawing.getAnchorOrInline().add(inline);
  51. return paragraph;
  52. }
  53. /**
  54. * 将图片从文件对象转换成字节数组.
  55. *
  56. * @param file  将要转换的文件
  57. * @return      包含图片字节数据的字节数组
  58. * @throws FileNotFoundException
  59. * @throws IOException
  60. */
  61. private static byte[] convertImageToByteArray(File file)
  62. throws FileNotFoundException, IOException {
  63. InputStream is = new FileInputStream(file );
  64. long length = file.length();
  65. // 不能使用long类型创建数组, 需要用int类型.
  66. if (length > Integer.MAX_VALUE) {
  67. System.out.println("File too large!!");
  68. }
  69. byte[] bytes = new byte[(int)length];
  70. int offset = 0;
  71. int numRead = 0;
  72. while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
  73. offset += numRead;
  74. }
  75. // 确认所有的字节都没读取
  76. if (offset < bytes.length) {
  77. System.out.println("Could not completely read file " +file.getName());
  78. }
  79. is.close();
  80. return bytes;
  81. }
  82. }

在表格中添加图片

现在我们知道了如何在文档中添加图片,如果在我们前面博客中的表格中添加图片将会使表格更漂亮。正如你将在下面看到的代码一样,并不比我们前面例子中所做的困难很多。

  1. public class AddingAnInlineImageToTable {
  2. private static WordprocessingMLPackage  wordMLPackage;
  3. private static ObjectFactory factory;
  4. /**
  5. *  首先我们创建包和对象工厂, 因此在类的随处我们都可以使用它们. 然后我们创建一个表格并添加
  6. *  边框. 接下来我们创建一个表格行并在第一个域添加一些文本.
  7. *  对于第二个域, 我们用与前面一样的图片创建一个段落并添加进去. 最后把行添加到表格中, 并将
  8. *  表格添加到包中, 然后保存这个包.
  9. */
  10. public static void main (String[] args) throws Exception {
  11. wordMLPackage = WordprocessingMLPackage.createPackage();
  12. factory = Context.getWmlObjectFactory();
  13. Tbl table = factory.createTbl();
  14. addBorders(table);
  15. Tr tr = factory.createTr();
  16. P paragraphOfText = wordMLPackage.getMainDocumentPart().createParagraphOfText("Field 1");
  17. addTableCell(tr, paragraphOfText);
  18. File file = new File("src/main/resources/iProfsLogo.png");
  19. P paragraphWithImage = addInlineImageToParagraph(createInlineImage(file));
  20. addTableCell(tr, paragraphWithImage);
  21. table.getContent().add(tr);
  22. wordMLPackage.getMainDocumentPart().addObject(table);
  23. wordMLPackage.save(new java.io.File("src/main/files/HelloWord8.docx"));
  24. }
  25. /**
  26. * 用给定的段落作为内容向给定的行中添加一个单元格.
  27. *
  28. * @param tr
  29. * @param paragraph
  30. */
  31. private static void addTableCell(Tr tr, P paragraph) {
  32. Tc tc1 = factory.createTc();
  33. tc1.getContent().add(paragraph);
  34. tr.getContent().add(tc1);
  35. }
  36. /**
  37. *  向新的段落中添加内联图片并返回这个段落.
  38. *  这个方法与前面例子中的方法没有区别.
  39. *
  40. * @param inline
  41. * @return
  42. */
  43. private static P addInlineImageToParagraph(Inline inline) {
  44. // Now add the in-line image to a paragraph
  45. ObjectFactory factory = new ObjectFactory();
  46. P paragraph = factory.createP();
  47. R run = factory.createR();
  48. paragraph.getContent().add(run);
  49. Drawing drawing = factory.createDrawing();
  50. run.getContent().add(drawing);
  51. drawing.getAnchorOrInline().add(inline);
  52. return paragraph;
  53. }
  54. /**
  55. * 使用给定的文件创建一个内联图片.
  56. * 跟前面例子中一样, 我们将文件转换成字节数组, 并用它创建一个内联图片.
  57. *
  58. * @param file
  59. * @return
  60. * @throws Exception
  61. */
  62. private static Inline createInlineImage(File file) throws Exception {
  63. byte[] bytes = convertImageToByteArray(file);
  64. BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
  65. int docPrId = 1;
  66. int cNvPrId = 2;
  67. return imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);
  68. }
  69. /**
  70. * 将图片从文件转换成字节数组.
  71. *
  72. * @param file
  73. * @return
  74. * @throws FileNotFoundException
  75. * @throws IOException
  76. */
  77. private static byte[] convertImageToByteArray(File file) throws FileNotFoundException, IOException {
  78. InputStream is = new FileInputStream(file );
  79. long length = file.length();
  80. // You cannot create an array using a long, it needs to be an int.
  81. if (length > Integer.MAX_VALUE) {
  82. System.out.println("File too large!!");
  83. }
  84. byte[] bytes = new byte[(int)length];
  85. int offset = 0;
  86. int numRead = 0;
  87. while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
  88. offset += numRead;
  89. }
  90. // Ensure all the bytes have been read
  91. if (offset < bytes.length) {
  92. System.out.println("Could not completely read file "+file.getName());
  93. }
  94. is.close();
  95. return bytes;
  96. }
  97. /**
  98. * 给表格添加简单的黑色边框.
  99. *
  100. * @param table
  101. */
  102. private static void addBorders(Tbl table) {
  103. table.setTblPr(new TblPr());
  104. CTBorder border = new CTBorder();
  105. border.setColor("auto");
  106. border.setSz(new BigInteger("4"));
  107. border.setSpace(new BigInteger("0"));
  108. border.setVal(STBorder.SINGLE);
  109. TblBorders borders = new TblBorders();
  110. borders.setBottom(border);
  111. borders.setLeft(border);
  112. borders.setRight(border);
  113. borders.setTop(border);
  114. borders.setInsideH(border);
  115. borders.setInsideV(border);
  116. table.getTblPr().setTblBorders(borders);
  117. }
  118. }

添加换页符

添加换页符相当地简单。Docx4j拥有一个叫作Br的break对象,这个对象有一个type属性,这种情况下我们需要将其设置为page,type其它可选的值为column和textWrapping。这个break可以很简单地添加到段落中。

  1. public class AddingAPageBreak {
  2. private static ObjectFactory factory;
  3. private static WordprocessingMLPackage  wordMLPackage;
  4. public static void main (String[] args) throws Docx4JException {
  5. wordMLPackage = WordprocessingMLPackage.createPackage();
  6. factory = Context.getWmlObjectFactory();
  7. wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");
  8. addPageBreak();
  9. wordMLPackage.getMainDocumentPart().addParagraphOfText("This is page 2!");
  10. wordMLPackage.save(new java.io.File("src/main/files/HelloWord11.docx") );
  11. }
  12. /**
  13. * 向文档添加一个换行符
  14. */
  15. private static void addPageBreak() {
  16. MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
  17. Br breakObj = new Br();
  18. breakObj.setType(STBrType.PAGE);
  19. P paragraph = factory.createP();
  20. paragraph.getContent().add(breakObj);
  21. documentPart.getJaxbElement().getBody().getContent().add(paragraph);
  22. }
  23. }

添加目录表

添加目录十分地简单。在Word中这是一个可以被添加的域,在Docx4j中是一样的。仅有一个你需要知道的小问题,就是像这样创建的一个文档,在你第一次打开它的时候,Word会给你一个此文档可能包含指向其它文件的域的提示信息,询问你是否要更新这些域。如果你点击了yes回应询问,Word将会将目录表域转换为真正的目录。如果你点击了no,你会看到这个文档没有目录。

在点击yes之后,这个文档会变成这个样子:

  1. public class AddingTableOfContent {
  2. private static ObjectFactory factory;
  3. /**
  4. *  首先我们创建对象工厂和包并从包中抽出文档部件. 然后我们添加目录表, 后面跟着一些带有分类
  5. *  标题样式的段落. 最后我们保存包.
  6. */
  7. public static void main(String[] args) throws Docx4JException {
  8. factory = Context.getWmlObjectFactory();
  9. WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
  10. MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
  11. addTableOfContent(documentPart);
  12. documentPart.addStyledParagraphOfText("Heading1", "Hello 1");
  13. documentPart.addStyledParagraphOfText("Heading2", "Hello 2");
  14. documentPart.addStyledParagraphOfText("Heading3", "Hello 3");
  15. documentPart.addStyledParagraphOfText("Heading1", "Hello 1");
  16. wordMLPackage.save(new File("src/main/files/HelloWord10.docx"));
  17. }
  18. /**
  19. *  将目录表添加到文档.
  20. *
  21. *  首先我们创建段落. 然后添加标记域开始的指示符, 然后添加域内容(真正的目录表), 接着添加域
  22. *  结束的指示符. 最后将段落添加到给定文档的JAXB元素中.
  23. *
  24. *  @param documentPart
  25. */
  26. private static void addTableOfContent(MainDocumentPart documentPart) {
  27. P paragraph = factory.createP();
  28. addFieldBegin(paragraph);
  29. addTableOfContentField(paragraph);
  30. addFieldEnd(paragraph);
  31. documentPart.getJaxbElement().getBody().getContent().add(paragraph);
  32. }
  33. /**
  34. * (不知道该怎么翻译, 因此将英文原注释保留)
  35. *  Adds the field that Word uses to create a table of content to the paragraph.
  36. *
  37. *  First we create a run and a text. Then we indicate that all spaces in the
  38. *  text are to be preserved and set the value to that of the TOC field.
  39. *  This field definition takes some arguments. The exact definition can be
  40. *  found in §17.16.5.58 of the Office Open XML standard. In this case we
  41. *  specify that we want to include all paragrapsh formatted with headings of
  42. *  levels 1-3 (\0 “1-3”). We also specify that we want all entries to be
  43. *  hyperlinks (\h), that we want to hide tab leader and page numbers in Web
  44. *  layout view (\z), and that we want to use the applied paragraph outline
  45. *  level (\u).
  46. *  Finally we take the text and use it to create a JAXB element containing text
  47. *  and add this to the run, which we then add to the given paragraph.
  48. *
  49. *  将Word用于创建目录表的域添加到段落中.
  50. *
  51. *  首先创建一个可运行块和一个文本. 然后指出文本中所有的空格都被保护并给TOC域设置值. 这个域定义
  52. *  需要一些参数, 确切定义可以在Office Open XML标准的§17.16.5.58找到, 这种情况我们指定所有
  53. *  段落使用1-3级别的标题来格式化(\0 "1-3"). 我们同时指定所有的实体作为超链接(\h), 而且在Web
  54. *  视图中隐藏标签和页码(\z), 我们要使用段落大纲级别应用(\u).
  55. *  最后使用文本对象创建了一个JAXB元素包含文本并添加到随后被添加到段落中的可运行块中.
  56. *
  57. *  @param paragraph
  58. */
  59. private static void addTableOfContentField(P paragraph) {
  60. R run = factory.createR();
  61. Text txt = new Text();
  62. txt.setSpace("preserve");
  63. txt.setValue("TOC \\o \"1-3\" \\h \\z \\u");
  64. run.getContent().add(factory.createRInstrText(txt));
  65. paragraph.getContent().add(run);
  66. }
  67. /**
  68. *  每个域都需要用复杂的域字符来确定界限. 本方法向给定段落添加在真正域之前的界定符.
  69. *
  70. *  再一次以创建一个可运行块开始, 然后创建一个域字符来标记域的起始并标记域是'脏的'因为我们想要
  71. *  在整个文档生成之后进行内容更新.
  72. *  最后将域字符转换成JAXB元素并将其添加到可运行块, 然后将可运行块添加到段落中.
  73. *
  74. *  @param paragraph
  75. */
  76. private static void addFieldBegin(P paragraph) {
  77. R run = factory.createR();
  78. FldChar fldchar = factory.createFldChar();
  79. fldchar.setFldCharType(STFldCharType.BEGIN);
  80. fldchar.setDirty(true);
  81. run.getContent().add(getWrappedFldChar(fldchar));
  82. paragraph.getContent().add(run);
  83. }
  84. /**
  85. *  每个域都需要用复杂的域字符来确定界限. 本方法向给定段落添加在真正域之后的界定符.
  86. *
  87. *  跟前面一样, 从创建可运行块开始, 然后创建域字符标记域的结束, 最后将域字符转换成JAXB元素并
  88. *  将其添加到可运行块, 可运行块再添加到段落中.
  89. *
  90. *  @param paragraph
  91. */
  92. private static void addFieldEnd(P paragraph) {
  93. R run = factory.createR();
  94. FldChar fldcharend = factory.createFldChar();
  95. fldcharend.setFldCharType(STFldCharType.END);
  96. run.getContent().add(getWrappedFldChar(fldcharend));
  97. paragraph.getContent().add(run);
  98. }
  99. /**
  100. *  创建包含给定复杂域字符的JAXBElement的便利方法.
  101. *
  102. *  @param fldchar
  103. *  @return
  104. */
  105. public static JAXBElement getWrappedFldChar(FldChar fldchar) {
  106. return new JAXBElement(new QName(Namespaces.NS_WORD12, "fldChar"), FldChar.class, fldchar);
  107. }
  108. }

总结

在本篇博客中我展示了如何添加图片和word文档中布局的两个方面。在我的下一篇博客我会继续介绍更多布局相关的示例,我本来是将它们添加到了本篇博客中,但是博客有点太长了。

向Docx4j生成的word文档添加图片和布局--第一部分的更多相关文章

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

    原文标题:Adding layout to your Docx4j-generated word documents, part 2 原文链接:http://blog.iprofs.nl/2012/1 ...

  2. C# 给Word文档添加内容控件

    C# 给Word文档添加内容控件 在MS Word中,我们可以通过内容控件来向word文档中插入预先定义好的模块,指定模块的内容格式(如图片.日期.列表或格式化的文本等),从而创建一个结构化的word ...

  3. OpenXml入门----给Word文档添加文字

    使用OpenXml给word文档添加文字,每个模块都有自己对于的属性以及内容,要设置样式就先声明属性对象,将样式Append到属性里面,再将属性append到模块里面,那么模块里面的内容就具备该样式了 ...

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

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

  5. java生成复杂word文档

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

  6. Java 如何给Word文档添加多行文字水印

    前言 我在以往的文章中曾介绍过如何给Word文档添加文本水印和图片水印,及怎样删除文档中的水印.关于文本水印,之前那篇教程里主要指的是单行字体的水印,而在操作Word文档时,有时也会碰到需要添加多行文 ...

  7. 判断pdf、word文档、图片等文件类型(格式)、大小的简便方法

    判断pdf.word文档.图片等文件类型(格式).大小的简便方法 很久没发文了,今天有时间就写一下吧. 关于上传文件,通常我们都需要对其进行判断,限制上传的类型,如果是上传图片,我们甚至会把图片转化成 ...

  8. 利用iTextSharp组件给PDF文档添加图片水印,文字水印

    最近在做关于PDF文档添加水印的功能,折腾了好久,终于好了.以下做个记录: 首先会用到iTextSharp组件,大家可以去官网下载,同时我也会在本文中附加进来. 代码中添加引用为:   using S ...

  9. 调用Microsoft.Office.Interop.Word生成自定义Word文档

    具体思路: 1.先制作Word模版,使用文本框+书签的方式来设计模版: 2.模版制作完之后,根据模版生成新文件,使用File.Copy方法,生成.doc格式新文件: 3.后台取得数据,参照网页渲染的方 ...

随机推荐

  1. WinInet 小例子

    #include <windows.h> #include <wininet.h> #include <string> #include <iostream& ...

  2. e674. 创建并绘制加速图像

    Images in accelerated memory are much faster to draw on the screen. This example demonstrates how to ...

  3. e552. 取Applet的参数

    An applet can be configured through the use of applet parameters. For example, the contents for a ne ...

  4. (转)sqlite3生成lib遇到的问题

    今天想用一用sqlite,但是下载后发现只有DLL,没有LIB,只能自己生成了.在H:/Program Files/Microsoft Visual Studio 8/VC/bin里面有个lib.ex ...

  5. Linux远程连接Windows桌面

    Ubuntu对初始用户的界面友好是有目共睹的:Fedora一向以创新.傲慢的姿态示人.其实,对于两者,我虽然更倾向于选择Fedora,但不可避免地会两者比较,发现Ubuntu更加人性化,这点是经过很多 ...

  6. el表达式取值优先级

    不同容器中存在同名值时,从作用范围小到大的顺序依次尝试取值:pageContext->request->session->application

  7. bootstrap中如何使input中的小图标获得点击事件

    bootstrap中,放入input中的小图标是不能点击的. 在表单中经常遇见密码旁边的眼睛图标点击后,可使密码可见. 要使小图标获得点击事件,可在小图标上覆盖一个跟小图标一样大的透明层,然后给透明层 ...

  8. mysql通过mysql_install_db初始化数据目录时使用--user选项的作用是什么?

    需求描述: mysql数据库通过mysql_install_db初始化数据目录时,使用了--user选项,这里记录下该参数的作用 参数解释: 1.--user的作用:就是以哪个操作系统用户来执行mys ...

  9. mysql的引擎myisam和innodb的区别

    1. MYISAM和INNODB的不同?答:主要有以下几点区别:   a)构造上的区别     MyISAM在磁盘上存储成三个文件,其中.frm文件存储表定义:.MYD (MYData)为数据文件:. ...

  10. UVa 10450 - World Cup Noise

    题目:构造一个01串,使得当中的1不相邻,问长度为n的串有多少中. 分析:数学,递推数列. 设长度为n的串有n个.则有递推关系:f(n)= f(n-1)+ f(n-2): 长度为n的结束可能是0或者1 ...