在 .docx 文档中使用 Groovy 脚本片段





5.00/5 (1投票)
docx 文档中功能齐全的 groovy 模板 scriptlets
引言
我最近的一个项目需要自动生成客户合同。合同是一份法律文件,大约有 10 页长。一份合同模板可以应用于许多客户,因此该文档是一个包含特定客户信息的模板。
在本文中,我将向您展示我如何解决这个问题。
要求
这是对正式需求的初始版本
指定的数据必须放置在复杂 DOC/DOCX 文件的标记位置.
需求随后得到了完善和扩展
- 指定的数据必须放置在复杂 DOCX 文件的标记位置。
- 输出标记必须类似于脚本片段:${}、<%%>、<%=%>。
- 输出数据可能不仅是字符串,还包括哈希表和对象。字段访问必须是一个选项。
- 输出语言必须简洁且对脚本友好:Groovy、JavaScript。
- 能够以表格形式显示对象列表,每个单元格显示一个字段。
背景
事实证明,该领域现有的产品(我说的是 Java 世界)并不符合初始要求。
产品简要概述
Jasper reports
Jasper Reports 使用 *.jrxml 文件作为模板。模板文件与输入数据(SQL 结果集或参数 Map)一起被交给处理器,该处理器可以生成以下任何格式:PDF、XML、HTML、CSV、XLS、RTF、TXT。
不符合要求
- 它不是所见即所得的,即使借助 iReport — 一个创建 jrxml 模板的视觉工具。
- 必须精通 JasperReports API 才能创建和样式化复杂的模板。
- JR 的输出格式不合适。PDF 可能还可以,但可手动编辑的能力更可取。
Docx4java
Docx4j 是一个用于创建和操作 Microsoft Open XML(Word docx、Powerpoint pptx 和 Excel xlsx)文件的 Java 库。
不符合要求
- 在 docx4java 的 文档 中没有符合我要求的情况。有一个关于 XMLUtils.unmarshallFromTemplate 功能的简短说明,但它只做最简单的替换。
- 输出的重复是通过准备好的 XML 源和 XPath 完成的, 链接。
Apache POI
Apache POI 是一个用于创建和操作 *.doc、*.ppt、*.xls 文件部分的 Java 工具。Apache POI API 的一个主要用途是文本提取应用程序,例如网络爬虫、索引生成器和内容管理系统。
不符合要求
- 没有符合我要求的功能。
Word Content Control Toolkit
Word Content Control Toolkit 是一个独立的、轻量级的工具,可以打开任何 Word Open XML 文档并列出其中的所有内容控件。
在我自己开发了带有脚本片段的解决方案后,我听说了一个基于该工具和 XSDT 转换组合的解决方案。它可能对某些人有用,但我没有深入研究,因为它使用我的解决方案更直接、步骤更少。
问题解决方案
很有趣!
1. 文档文本内容存储在 zip 归档中的 Open XML 文件里。传统的 JDK 6 压缩器不支持显式的编码参数。也就是说,使用此压缩器可能会产生损坏的 docx 文件。我不得不使用 Groovy 包装器 AntBuilder 进行压缩,它确实有编码参数。
2. 在 MS Word 中输入的任何文本都可以“任意”拆分成用 XML 包装的部分。因此,我必须解决清理从模板 XML 生成的填充问题。我为此任务使用了正则表达式。我没有尝试使用 XSLT 或其他方法,因为我认为 RegEx 会更快。
3. 我决定使用 Groovy 作为脚本语言,因为它简单、具有 Java 特性,并且内置了 模板处理器。我发现了一个与该处理器相关的有趣问题。事实证明,即使在一个小的 10 页文档中,也很容易遇到两个脚本片段之间字符串长度的限制。
我不得不将两个脚本片段之间的文本替换为 UUID 字符串,使用修改后的文本运行 Groovy 模板处理器,最后用初始文本片段替换这些 UUID 占位符。
克服这些困难后,我在实际生活中试用了该项目。结果非常好!
我创建了一个项目网站并发布了它。
项目地址:snowindy.github.com/scriptlet4docx/
代码示例
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("name","John");
params.put("sirname","Smith");
DocxTemplater docxTemplater = new DocxTemplater(new File("path_to_docx_template/template.docx"));
docxTemplater.process(new File("path_to_result_docx/result.docx"), params);
脚本片段类型说明
${ data }
相当于 out.print(data)
<%= data %>
相当于 out.print(data)
<% any_code %>
评估包含的代码。不输出。可用于条件分支
<% if (cond) { %> This text block will be printed in case of "cond == true" <% } else { %> This text block will be printed otherwise. <% } %>
$[ @listVar.field ]
这是一种自定义 Scriptlet4docx 脚本片段类型,用于将对象集合输出到 docx 表格。它必须在表格单元格内使用。
假设我们有一个 person 对象列表。每个对象都有两个字段:“name”和“address”。我们想将它们输出到一个两列的表格中。
- 创建一个绑定,键为“personList”,引用该集合。
- 在模板 docx 文档中创建一个两列的表格:两列,一行。
- 第一个单元格为 $[@person.name];第二个为 $[@person.address]。
- 瞧,整个集合都会打印到表格中。
实时模板示例
您可以在 演示模板 中查看所有提到的脚本片段的用法。
项目未来
如果我真的开发了一种新的 docx 模板处理方法,我会乐意推广它。
项目待办事项
- 预处理模板缓存,
- 列表中脚本片段的支持
- 流式 API
注:英语不是我的母语,请原谅我可能犯的错误。