title: Java操作Word文档
description: 使用 Spire.Doc for Java操作Word文档;替换Word模板中的占位符,然后生成新的Word文档
tags: [工具]
categories:
- 其他
Java 生成 Word 的几种方案
参考
主要有这么一些工具可以使用 Jacob、Apache POI、Java2word、iText,还有一种方法是使用XML作为模板。
使用 XML 的思路:先用 office 编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析 FreeMarker模板 并输出 Word 文档。经测试这样方式生成的word文档完全符合 office 标准,样式、内容控制非常便利,打印也不会变形,生成的文档和office中编辑文档完全一样。 ===> 参考
- Jacob 和 Java2word需要服务器端支持 office,相对麻烦。
- Apache POI 功能较少。===> 参考
这里推荐一个刚刚发现的产品,由成都冰蓝科技开发的产品 Spire,可以用于操作各种文档,目前我只体验过 Spire.Doc for Java,个人认为使用简单,功能强大,对样式的支持非常好(虽然Spire主要面向企业提供产品,但是也提供了免费使用的产品)。最大的缺点是,免费版,没有 Api 文档且Jar包不能完美反编译,只有使用文档,有些代码只能依靠名称推断作用,所以很难深入的挖掘 Spire 的用法。
使用 Spire.Doc 替换 Word 模板中占位符
这里使用 Maven 下载 Spire.Doc 也可以直接下载 jar 包 ==> 参考
1️⃣:首先引入 Maven 依赖
在 Maven 的官方仓库或者阿里云镜像仓库中是没有 Spire 依赖的,所以需要配置 Spire 自己的一个仓库源。
<repositories>
<repository>
<id>com.e-iceblue</id>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
以下就是 Spire.Doc for Java 的依赖
<!--spire.doc 操作word文档-->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.doc.free</artifactId>
<version>3.9.0</version>
</dependency>
2️⃣:设计一个Word模板,此文档中存在占位符
3️⃣:编码
public class TestSpireWord {
@Test
public void generateAndReplaceText() {
Document doc = new Document();
doc.loadFromFile("F:\\yourlocation\\template.docx", FileFormat.Docx);
Map<String, String> map = new HashMap<>();
map.put("${name}", "张山");
map.put("${birthday}", "2021-10-18");
map.put("${result}", "成功");
map.put("${col}", "第一列");
map.put("${col1}", "第二列");
map.put("${col2}", "第三列");
replaceSpecialWord(doc, map);
// 保存为文件
doc.saveToFile("F:\\yourlocation\\result.docx",FileFormat.Docx);
// 或者保存至输出流中
//ByteArrayOutputStream os = new ByteArrayOutputStream();
//doc.saveToStream(os, FileFormat.Docx);
}
/**
* 替换Word文件中 ${} 标识的特殊字符
* <br>
* <strong>注意:如果存在部分特殊表示无法替换,请尝试将 ${} 的整个字符串复制到word中,有可能word没有将${}识别为一个整体</strong>
* @param doc: Sprire Document
* @param map: 占位符${} 与 需要替换的为字符串的对应关系
*/
public void replaceSpecialWord(Document doc, Map<String, String> map) {
// 正则表达式,匹配所有的占位符 ${}
Pattern pattern = Pattern.compile("\\$\\{.*?}");
// 根据正则表达式获取所有文本
TextSelection[] allPattern = doc.findAllPattern(pattern);
// 逐个替换占位符
for (TextSelection textSelection : allPattern) {
String tmp = map.get(textSelection.getSelectedText());
System.out.print(textSelection.getSelectedText());
int res = doc.replace(textSelection.getSelectedText(), tmp, true, true);
System.out.println(": " + res);
}
}
}
最终结果:
// 控制台输出
${name}: 1
${birthday}: 1
${result}: 1
${col}: 1
${col1}: 1
${col2}: 1
可以看到使用 Spire.Doc for Java 替换占位符十分简单,并且不会破坏模板的原有样 式。
如果需要了解 Spire.Doc for Java 的更多操作,参考 ===> 官方文档
使用Spire.Doc 将Word文档中占位符替换为图片
1️⃣:编写一个测试文档
2️⃣:了解Sprie.Doc 是如何插入一张图片
参考—官方文档
Spire.Doc for Java中的 Paragraph类 提供了appendPicture ()方法用于给Word段落添加图片。
public class InsertImage {
public static void main(String[] args){
//创建Document对象
Document doc = new Document();
//添加节
Section section = doc.addSection();
//添加第一个段落
Paragraph paragraph = section.addParagraph();
//添加图片到段落
DocPicture picture = paragraph.appendPicture("C:\\Users\\Administrator\\Desktop\\Hydrangeas.jpg");
//设置图片宽度
picture.setWidth(300f);
//设置图片高度
picture.setHeight(250f);
//给段落设置水平居中对齐方式
paragraph.getFormat().setHorizontalAlignment(HorizontalAlignment.Center);
//保存
doc.saveToFile("InsertImage.docx", FileFormat.Docx_2013);
}
}
第二种方式: ===> 参考
通过获取 Spire.Doc 中的 TextRange 对象,可以用这个对象插入图片。
Document doc = new Document();
doc.loadFromFile("F:\\yourlocation\\template.docx", FileFormat.Docx); // 或者可以加载流loadFromStream(.,.)
// 0. 使用 Spire.Doc 的 DocPicture类 加载一张图片,并且可以设置图片的宽高
DocPicture pic = new DocPicture(doc);
pic.loadImage(imgList.get(count));
// 1. 获取 TextSelection
TextSelection selection = doc.findString("matchString", true, true); // 第一个参数是需要的匹配字符串即占位符,后面两个参数的意思为:是否大小写敏感、是否匹配整个单词
// 2. 根据 selection 获取 TextRange
TextRange range = selection.getAsOneRange();
// 3. 通过 TextRange 找到 占位符在一个段落中的位置
int index = range.getOwnerParagraph().getChildObjects().indexOf(range);
// 4. 通过获取到的 index 位置,插入一张图片
range.getOwnerParagraph().getChildObjects().insert(index, pic);
range.getOwnerParagraph().getChildObjects().remove(range); // 这个remove 操作具体作用不太清楚,但是没有这一步图片是无法正确插入的
3️⃣:开始编码
// 测试方法
@Test
public void imageTest() {
Document doc = new Document();
doc.loadFromFile("F:\\yourlocation\\template.docx", FileFormat.Docx);
File file = new File("F:\\yourlocation\\orange.jpg");
File file1 = new File("F:\\yourlocation\\orange.jpg");
try {
InputStream in = new FileInputStream(file);
InputStream in1 = new FileInputStream(file1);
List<InputStream> imgList = new ArrayList<>();
imgList.add(in);
imgList.add(in1);
String matchString = "${images}";
replaceTextToImage(doc, matchString, imgList);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
doc.saveToFile("F:\\yourlocation\\result.docx",FileFormat.Docx);
}
/**
* 替换Word文档中的同一占位符,为多张不同的图片,每一个占位符对应一张图片,
* 如果占位符比图片数量多则该占位符会被写为空字符串
* @param doc: Spire Document
* @param matchString: 占位符
* @param imgList: 图片输入流集合
* @date 2021/10/22 20:09
*/
public void replaceTextToImage(Document doc, String matchString, List<InputStream> imgList) {
TextSelection[] selections = doc.findAllString(matchString, true, true);
int total = imgList.size();
int count = 0;
for (TextSelection selection : selections) {
if (count < total && imgList.get(count) != null) {
DocPicture pic = new DocPicture(doc);
pic.loadImage(imgList.get(count));
pic.setWidth(100f); // 设置图片宽高
pic.setHeight(100f);
TextRange range = selection.getAsOneRange();
int index = range.getOwnerParagraph().getChildObjects().indexOf(range);
range.getOwnerParagraph().getChildObjects().insert(index, pic);
range.getOwnerParagraph().getChildObjects().remove(range);
count++;
} else {
// 如果已近没有了图片则将所有占位符替换为空
doc.replace(selection.getSelectedText(), "", true, true);
break;
}
}
}
/**
* 按照横纵比缩放图片
* @param pic: Spire 图片对象
* @param rate: 缩放比例 0.1<= rate <=10,
* @author He Peng
* @date 2021/10/23 9:28
*/
public static void zoomImage(DocPicture pic, float rate) {
if (rate < 0.1 || rate > 10) {
throw new IllegalArgumentException("缩放比例参数的数值范围为:0.1<= rate <=10,不能超过此范围");
}
float originalWidth = pic.getWidth();
float originalHeight = pic.getHeight();
pic.setWidth(originalWidth * rate);
pic.setHeight(originalHeight * rate);
}
最终结果:
📘 参考资料
个人博客同步发布
POI操作word模板并生成新的word–jianshu
Spire.doc for Java—官方文档
Spire.Doc for Java 资源—csdn
通过 Maven 仓库安装 Spire 系列 Java 产品—官方文档
Spire.Doc 系列教程