带有docx4j的Java Word(.docx)文档

几个月前,我需要创建一个包含许多表和段落的动态Word文档。 过去,我曾使用POI来实现此目的,但是我发现它很难使用,并且在创建更复杂的文档时对我来说效果不佳。 因此,对于这个项目,经过一番搜索,我决定使用docx4j 。 Docx4j,根据他们的网站是:

“ docx4j是一个Java库,用于创建和处理Microsoft Open XML(Word docx,Powerpoint pptx和Excel xlsx)文件。
它类似于Microsoft的OpenXML SDK,但适用于Java。

在本文中,我将向您展示几个示例,您可以使用这些示例来生成Word文档的内容。 更具体地说,我们将看以下两个示例:

  • 加载模板Word文档以添加内容并另存为新文档
  • 将段落添加到此模板文档
  • 将表添加到此模板文档

这里的一般方法是首先创建一个Word文档,其中包含最终文档的布局和主要样式。 在本文档中,您将需要添加占位符(简单字符串),我们将使用这些占位符来搜索并替换为真实内容。

例如,一个非常基本的模板如下所示:

在本文中,我们将向您展示如何填写此内容,以便获得:

加载模板Word文档以添加内容并另存为新文档

首先是第一件事。 让我们创建一个简单的Word文档,将其用作模板。 为此,只需打开Word,创建一个新文档并将其另存为template.docx。 这是我们用来向其添加内容的单词模板。 我们需要做的第一件事是用docx4j加载该文档。 您可以使用以下一段Java代码:

private WordprocessingMLPackage getTemplate(String name) throws Docx4JException, FileNotFoundException {WordprocessingMLPackage template = WordprocessingMLPackage.load(new FileInputStream(new File(name)));return template;}

这将返回一个Java对象,该对象表示完整的(此时)空文档。 现在,我们可以使用Docx4J API在此Word文档中添加,删除和修改内容。 Docx4J有许多帮助程序类,可用于遍历此文档。 我确实写了一些帮助程序,尽管它们确实使查找特定的占位符并将其替换为实际内容变得非常容易。 让我们看看其中之一。 该操作是对几个JAXB操作的包装,使您可以搜索特定元素及其所有子元素来查找某个类。 例如,您可以使用它来获取文档中的所有表,表中的所有行等等。

private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {List<Object> result = new ArrayList<Object>();if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();if (obj.getClass().equals(toSearch))result.add(obj);else if (obj instanceof ContentAccessor) {List<?> children = ((ContentAccessor) obj).getContent();for (Object child : children) {result.addAll(getAllElementFromObject(child, toSearch));}}return result;}

没什么复杂的,但真的很有帮助。 让我们看看如何使用此操作。 对于此示例,我们将使用不同的值替换一个简单的文本占位符。 例如,这是您用来动态设置文档标题的内容。 不过,首先,在您创建的Word模板中添加一个自定义占位符。 我将为此使用SJ_EX1。 我们将用我们的名字替换这个值。 docx4j中的基本文本元素由org.docx4j.wml.Text类表示。 要替换此简单的占位符,我们要做的就是调用此方法:

private void replacePlaceholder(WordprocessingMLPackage template, String name, String placeholder ) {List<Object> texts = getAllElementFromObject(template.getMainDocumentPart(), Text.class);for (Object text : texts) {Text textElement = (Text) text;if (textElement.getValue().equals(placeholder)) {textElement.setValue(name);}}}

这将查找文档中的所有Text元素,并将匹配的元素替换为我们指定的值。 现在,我们要做的就是将文档写回到文件中。

private void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException {File f = new File(target);template.save(f);}

如您所见,并不难。

通过此设置,我们还可以将更复杂的内容添加到Word文档中。 确定如何添加特定内容的最简单方法是查看word文档的XML源代码。 这将告诉您需要哪些包装器以及Word如何编组XML。 对于下一个示例,我们将研究如何添加完整的段落。  

将段落添加到此模板文档

您可能想知道为什么我们需要添加段落? 我们已经可以添加文本了,一个段落不只是一大段文本吗? 好,是的,不是。 一段确实看起来像是一段很大的文本,但是您需要考虑的是换行符。 如果像我们之前那样添加Text元素,并在文本中添加换行符,它们将不会显示。 需要换行符时,需要创建一个新段落。 幸运的是,使用Docx4j也很容易做到这一点。
我们将通过以下步骤进行操作:

  1. 从模板中找到要替换的段落
  2. 将输入文本分成单独的行
  3. 对于每一行,根据模板中的段落创建一个新段落
  4. 删除原始段落

我们应该已经拥有的辅助方法不应该太难了。

private void replaceParagraph(String placeholder, String textToAdd, WordprocessingMLPackage template, ContentAccessor addTo) {// 1. get the paragraphList<Object> paragraphs = getAllElementFromObject(template.getMainDocumentPart(), P.class);P toReplace = null;for (Object p : paragraphs) {List<Object> texts = getAllElementFromObject(p, Text.class);for (Object t : texts) {Text content = (Text) t;if (content.getValue().equals(placeholder)) {toReplace = (P) p;break;}}}// we now have the paragraph that contains our placeholder: toReplace// 2. split into seperate linesString as[] = StringUtils.splitPreserveAllTokens(textToAdd, '\n');for (int i = 0; i < as.length; i++) {String ptext = as[i];// 3. copy the found paragraph to keep styling correctP copy = (P) XmlUtils.deepCopy(toReplace);// replace the text elements from the copyList texts = getAllElementFromObject(copy, Text.class);if (texts.size() > 0) {Text textToReplace = (Text) texts.get(0);textToReplace.setValue(ptext);}// add the paragraph to the documentaddTo.getContent().add(copy);}// 4. remove the original one((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);}

在此方法中,我们用提供的文本替换段落的内容,然后将新段落替换为用addTo指定的参数。

String placeholder = "SJ_EX1";String toAdd = "jos\ndirksen";replaceParagraph(placeholder, toAdd, template, template.getMainDocumentPart());

如果您在Word模板中使用更多内容来运行此程序,则会注意到这些段落将出现在文档的底部。 原因是将段落添加回了主文档。 如果您希望将段落添加到文档中的特定位置(通常需要这样做),则可以将其包装在1×1无边界表格中。 该表被视为段落的父级,可以在此处添加新段落。

将表添加到此模板文档

我想展示的最后一个示例是如何向单词模板添加表格。 实际上,更好的描述是如何在Word模板中填充预定义的表格。 就像我们对简单的文本和段落所做的一样,我们将替换占位符。 对于此示例,向您的Word文档中添加一个简单的表格(您可以随意设置样式)。 向此表添加1个哑行,用作内容模板。 在代码中,我们将查找该行,将其复制,并将内容替换为来自Java代码的新行,如下所示:

  1. 查找包含我们的关键字之一的表
  2. 复制用作行模板的行
  3. 对于每行数据,根据行模板向表中添加一行
  4. 删除原始模板行

与我们在段落中显示的方法相同。 首先,让我们看一下如何提供替换数据。 对于此示例,我仅提供了一组哈希图,其中包含要替换的占位符的名称和要替换为其的值。 我还提供了可在表格行中找到的替换令牌。

Map<String,String> repl1 = new HashMap<String, String>();repl1.put("SJ_FUNCTION", "function1");repl1.put("SJ_DESC", "desc1");repl1.put("SJ_PERIOD", "period1");Map<String,String> repl2 = new HashMap<String,String>();repl2.put("SJ_FUNCTION", "function2");repl2.put("SJ_DESC", "desc2");repl2.put("SJ_PERIOD", "period2");Map<String,String> repl3 = new HashMap<String,String>();repl3.put("SJ_FUNCTION", "function3");repl3.put("SJ_DESC", "desc3");repl3.put("SJ_PERIOD", "period3");replaceTable(new String[]{"SJ_FUNCTION","SJ_DESC","SJ_PERIOD"}, Arrays.asList(repl1,repl2,repl3), template);

现在,这个replaceTable方法是什么样的。

private void replaceTable(String[] placeholders, List<Map<String, String>> textToAdd,WordprocessingMLPackage template) throws Docx4JException, JAXBException {List<Object> tables = getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);// 1. find the tableTbl tempTable = getTemplateTable(tables, placeholders[0]);List<Object> rows = getAllElementFromObject(tempTable, Tr.class);// first row is header, second row is contentif (rows.size() == 2) {// this is our template rowTr templateRow = (Tr) rows.get(1);for (Map<String, String> replacements : textToAdd) {// 2 and 3 are done in this methodaddRowToTable(tempTable, templateRow, replacements);}// 4. remove the template rowtempTable.getContent().remove(templateRow);}}

此方法查找表,获取第一行,并为每个提供的地图在表中添加新行。 返回之前,它将删除模板行。 此方法使用两个帮助器:addRowToTable和getTemplateTable。 我们首先来看最后一个:

private Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {for (Iterator<Object> iterator = tables.iterator(); iterator.hasNext();) {Object tbl = iterator.next();List<?> textElements = getAllElementFromObject(tbl, Text.class);for (Object text : textElements) {Text textElement = (Text) text;if (textElement.getValue() != null && textElement.getValue().equals(templateKey))return (Tbl) tbl;}}return null;}

此函数只是查看表是否包含我们的占位符之一。 如果是这样,则返回该表。 addRowToTable操作也非常简单。

private static void addRowToTable(Tbl reviewtable, Tr templateRow, Map<String, String> replacements) {Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);List textElements = getAllElementFromObject(workingRow, Text.class);for (Object object : textElements) {Text text = (Text) object;String replacementValue = (String) replacements.get(text.getValue());if (replacementValue != null)text.setValue(replacementValue);}reviewtable.getContent().add(workingRow);}

此方法复制我们的模板,并使用提供的值替换此模板行中的占位符。 该副本将添加到表中。 就是这样。 通过这段代码,我们可以在Word文档中填写套利表,同时保留表的布局和样式。

到本文为止。 使用段落和表格,您可以创建许多不同类型的文档,这与最常生成的文档类型非常匹配。 但是,也可以使用这种方法将其他类型的内容添加到Word文档中。

参考:来自Smart Java博客的JCG合作伙伴 Jos Dirksen 使用docx4j以编程方式创建复杂的Word(.docx)文档 。


翻译自: https://www.javacodegeeks.com/2012/07/java-word-docx-documents-with-docx4j.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372155.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql中distinct关键字,MySQL关键字Distinct的详细介绍

DDLPrepare SQL&#xff1a;?Prepare Data&#xff1a;?查询数据如下图所示&#xff1a;第一种情况&#xff0c;使用Distinct关键字&#xff0c;查询单列数据&#xff0c;如下图所示&#xff1a;结果&#xff1a;对 name 字段进行去重处理&#xff0c;符合预期期望&#xff0…

#pragma 预处理指令

Linux C 编程一站式学习 #pragma 预处理指示供编译器实现一些非标准的特性&#xff0c;C 标准没有规定 #pragma 后面应该写什么以及起什么作用&#xff0c;由编译器自己规定。有的编译器用 #pragma 定义一些特殊功能寄存器名&#xff0c;有的编译器用 #pragma 定位链接地址&…

px ,em ,rem

做移动端或者响应式的页面必然需要字体的变化的。这次我就自己的经验来说说他们之间的关系&#xff0c;以及怎么用。 px (绝对单位)是我们常用的就不说了。 em&#xff08;相对单位&#xff0c;相对父级&#xff09; em 指字体高&#xff0c;任意浏览器的默认字体高都是16px。所…

使用JAnnocessor生成Java代码

在本文中&#xff0c;我将向你展示如何生成的代码JAnnocessor通过创建框架Nikolche Mihajlovski 。 在Nikolche的演讲中&#xff0c;我第一次在GeeCON 2012大会上遇到JAnnocessor&#xff1a; “创新和实用的Java源代码生成” &#xff08;幻灯片&#xff09; 。 之后&#xff…

Linq学习笔记(转)

开始Linq前你要知道的 扩展方法 顾名思义就是对现有类进行扩展的的方法&#xff0c;扩展方法可以在不修改现有类的情况下&#xff0c;为现有类增加公共的接口&#xff08;不是C#中的interface&#xff09;。 扩展方法本质上是一个静态方法&#xff0c;不同之处在于它的第一个参…

cass展点不在原位置,cass中打开一副图后,通过绘图处理-——展高程点,怎么之前的图形就不显示了,,只剩下高程点!!...

答&#xff1a;1、进入控制面板&#xff0c;选择“卸载或更改程序”。 2、选中“AutoCAD2006”图标。 3、右击选择“更改”。 4、进入“AutoCAD2006安装程序对话框”&#xff0c;选择“添加/删除功能”单选按钮&#xff0c;点击下一步。 5、在“程序文件”列表中&#xff0c;选…

(二)windows下安装PHPCMS V9

一、准备工作 搭建环境 &#xff1a;参考:Windows下搭建PHP开发环境及相关注意事项PHPCMS V9 &#xff1a;下载适合自己 PHPCMS V9 版本到本地或服务器&#xff0c;下载地址&#xff1a;http://www.phpcms.cn/html/download/说明&#xff1a;官方提供了 2 种不同的编码。包括 G…

JavaFX 2.0布局窗格– HBox和VBox

如果要对JavaFX 2.0中所有不同的布局窗格进行概述&#xff0c;或者想了解有关它们的一些基本知识&#xff0c;请参阅我以前的文章《 JavaFX 2.0中的布局窗格》 。 布局窗格HBox和VBox绝对是JavaFX 2.0中最基本的布局容器。 如您所知&#xff0c;它们的用途是将所有子级布置在一…

flask mysql分页,Flask分页的实现方法

所需环境Flask-SQLAlchemy分页使用Flask-SQLAlchemy提供的pagination()方法。页数是pagination()方法的第一个参数&#xff0c;也是唯一必须的参数。可选参数per_page用来指定每页显示的记录数。参考代码&#xff1a;def index():# ...page request.args.get(page, 1, typeint…

Java中的生成器设计模式

Java 中的 Builder设计模式是一种创建模式&#xff0c;即用于创建对象&#xff0c;类似于 工厂方法设计模式 &#xff0c;这也是创建设计模式。 在学习任何设计模式之前&#xff0c;我建议先找出特定设计模式要解决的问题。 众所周知&#xff0c; 必要性是发明的母亲。 在没有面…

验证码( 随机数)

方式一&#xff08;变色版&#xff09;&#xff1a; <html> <head><meta charset"UTF-8"/><title></title><script src"jquery-2.0.2.min.js"></script> </head> <body> <?php header("co…

单片机串行通信全解析

1.什么是串行通信&#xff1f; 串行通信&#xff08;英语&#xff1a;Serial communication&#xff09;是指在计算机总线或其他数据通道上&#xff0c;每次传输一个位元数据&#xff0c;并连续进行以上单次过程的通信方式。与之对应的是并行通信&#xff0c;它在串行端口上通过…

java type 类型,java中的泛型类型与Type接口

假设我们定义了一个Room的类&#xff0c;表示一个房间public classRoom(){}由于我们建造好房间是&#xff0c;不知道房间以后的用途&#xff0c;他可能用来住人&#xff0c;也有可能用来放货物&#xff0c;因此需要用到泛型。但是我们可能想获取Room这个房间里面进来的的东西的…

centos7下操作防火墙

引言 最近使用centos7系统比较频繁&#xff0c;在配置服务器的时候&#xff0c;总是遇到能够ping通服务器&#xff0c;但是就是没有办法访问80端口&#xff0c;这个时候我的直觉告诉我&#xff0c;肯定是防火墙的原因&#xff0c;但是使用iptables却怎么都找不到命令&#xff0…

其他团队对本团队评价的总结

我们小组在看了其他小组的评价后&#xff0c;对自己的程序有了新的看法。转载于:https://www.cnblogs.com/bk1246788/p/6879691.html

Java:使用Fork / Join框架的Mergesort

此项的目的是显示一个Fork / Join RecursiveAction的简单示例&#xff0c;而不是过多地研究合并合并的可能优化方法&#xff0c;或者比使用Exkutor / Join Pool优于现有的基于Java 6的现有实现&#xff08;例如ExecutorService&#xff09;的相对优势。 以下是使用Java的自上而…

php的异常处理方式,php异常处理基本方法

当一个php脚本运行时&#xff0c;为了防止脚本运行崩溃&#xff0c;亦或是当php作为webserver&#xff0c;为了防止php程序出错&#xff0c;抛出httpcode500错误&#xff0c;我们常常需要对php程序做异常处理。今天介绍的是最基本的异常处理方法&#xff1a;一般而言&#xff0…

关系型数据库的三范式

第一范式:确保每列的原子性. 如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式. 例如:顾客表(姓名、编号、地址、……)其中"地址"列还可以细分为国家、省、市、区等。第二范式:在第一范式的基础上更进一层,目标是确保表…

vray学习笔记(3)-多维子材质是个什么东西

多维子材质是个什么东西&#xff1f;为什么出现这个概念&#xff1f; 在3dsmax官方网站&#xff0c;我们可以看到它的定义&#xff1a; The Multi/Sub-Object material lets you assign different materials at the sub-object level of your geometry. 意思是多维子材质这个概…

Hello JavaFX 2.0:命令行介绍

我从博客文章Hello JavaFX 2.0&#xff1a;NetBeans 7.1 beta的介绍中&#xff0c;从NetBeans 7.1 beta的角度看了一个无处不在的Hello World示例的简单JavaFX版本。 在本文中&#xff0c;我将介绍仅使用命令行工具通过JavaFX实现的Hello World版本。 JavaFX 2.0 API文档包括ja…