65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2012年5月2日

CPOL

4分钟阅读

viewsIcon

21436

docx 文档中功能齐全的 groovy 模板 scriptlets

引言

我最近的一个项目需要自动生成客户合同。合同是一份法律文件,大约有 10 页长。一份合同模板可以应用于许多客户,因此该文档是一个包含特定客户信息的模板。

在本文中,我将向您展示我如何解决这个问题。 

要求

这是对正式需求的初始版本

指定的数据必须放置在复杂 DOC/DOCX 文件的标记位置.

需求随后得到了完善和扩展

  1. 指定的数据必须放置在复杂 DOCX 文件的标记位置。
  2. 输出标记必须类似于脚本片段:${}、<%%>、<%=%>。
  3. 输出数据可能不仅是字符串,还包括哈希表和对象。字段访问必须是一个选项。
  4. 输出语言必须简洁且对脚本友好:Groovy、JavaScript。
  5. 能够以表格形式显示对象列表,每个单元格显示一个字段。

背景

事实证明,该领域现有的产品(我说的是 Java 世界)并不符合初始要求。

产品简要概述

Jasper reports

Jasper Reports 使用 *.jrxml 文件作为模板。模板文件与输入数据(SQL 结果集或参数 Map)一起被交给处理器,该处理器可以生成以下任何格式:PDF、XML、HTML、CSV、XLS、RTF、TXT。

不符合要求

  1. 它不是所见即所得的,即使借助 iReport — 一个创建 jrxml 模板的视觉工具。
  2. 必须精通 JasperReports API 才能创建和样式化复杂的模板。
  3. JR 的输出格式不合适。PDF 可能还可以,但可手动编辑的能力更可取。

Docx4java

Docx4j 是一个用于创建和操作 Microsoft Open XML(Word docx、Powerpoint pptx 和 Excel xlsx)文件的 Java 库。

不符合要求

  1. 在 docx4java 的 文档 中没有符合我要求的情况。有一个关于 XMLUtils.unmarshallFromTemplate 功能的简短说明,但它只做最简单的替换。
  2. 输出的重复是通过准备好的 XML 源和 XPath 完成的,  链接

Apache POI

Apache POI 是一个用于创建和操作 *.doc、*.ppt、*.xls 文件部分的 Java 工具。Apache POI API 的一个主要用途是文本提取应用程序,例如网络爬虫、索引生成器和内容管理系统。

不符合要求

  1. 没有符合我要求的功能。

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”。我们想将它们输出到一个两列的表格中。

  1. 创建一个绑定,键为“personList”,引用该集合。
  2. 在模板 docx 文档中创建一个两列的表格:两列,一行。
  3. 第一个单元格为 $[@person.name];第二个为 $[@person.address]。
  4. 瞧,整个集合都会打印到表格中。

实时模板示例

您可以在 演示模板 中查看所有提到的脚本片段的用法。

项目未来

如果我真的开发了一种新的 docx 模板处理方法,我会乐意推广它。

项目待办事项

  1. 预处理模板缓存,
  2. 列表中脚本片段的支持
  3. 流式 API

 

 注:英语不是我的母语,请原谅我可能犯的错误。 

© . All rights reserved.