1.word模板制作
- 在word文档光标位置,插入 -》文档部件 -》域,弹框如下:
最终生成的模板文档如下:
注:图片域和单个变量域是同一个,定义图片域时,同一个图片需要定义不同的域名。
2.工具类准备
- 使用Aspose进行文档转换,首先引入相应的jar包到系统环境
- 项目resource下导入license.xml文件
- 使用Aspose时,需要调用设置License方法,设置完成第一次替换比较慢,再次就不需要设置License方法,效率会更高。
注意:使用Aspose时,每一个模块(words,pdf,cells)都有相同的类,如License类,SaveOptions类,SaveFormat类,功能各不相同。
获取license示例代码:
package com.sinoif.common.utils;
import com.aspose.words.License;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
/**
* @description:
* @author: guanlj
* @date: 2020/7/24 11:10
*/
@Slf4j
public class AsposeLicenseUtil {
private static License aposeLic = new License();
/**
* 获取License的输入流
*
* @return
*/
private static InputStream getLicenseInput() {
InputStream inputStream = null;
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
//获取项目resource下的文件流
inputStream = contextClassLoader.getResourceAsStream("license.xml");
} catch (Exception e) {
log.error("license not found!"+ e);
}
return inputStream;
}
/**
* 设置License
*
* @return true表示已成功设置License, false表示失败
*/
public static boolean setWordsLicense() {
if (!aposeLic.getIsLicensed()) {
try {
aposeLic.setLicense(getLicenseInput());
return aposeLic.getIsLicensed();
} catch (Exception e) {
log.error("set words license error!", e);
}
}
return aposeLic.getIsLicensed();
}
}
3.模板填充
代码示例:
package com.sinoif.common.utils;
import com.aspose.words.*;
import com.aspose.words.net.System.Data.DataRow;
import com.aspose.words.net.System.Data.DataTable;
import lombok.extern.slf4j.Slf4j;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author guanlj
* @date 2020/01/22
*/
@Slf4j
public class ContractUtil {
/** 方框value */
public static String CODE_BOX = "codeBox";
/** 方框打勾value */
public static String CODE_TICK = "codeTick";
private ContractUtil() {
}
/**
* 调整bufferedimage大小
* @param source BufferedImage 原始image
* @param targetW int 目标宽
* @param targetH int 目标高
* @param flag boolean 是否同比例调整
* @return BufferedImage 返回新image
*/
public static BufferedImage resizeBufferedImage(BufferedImage source, int targetW, int targetH, boolean flag) {
int type = source.getType();
BufferedImage target = null;
double sx = (double) targetW / source.getWidth();
double sy = (double) targetH / source.getHeight();
if (flag && sx > sy) {
sx = sy;
targetW = (int) (sx * source.getWidth());
} else if(flag && sx <= sy){
sy = sx;
targetH = (int) (sy * source.getHeight());
}
// TYPE_CUSTOM
if (type == BufferedImage.TYPE_CUSTOM) {
ColorModel cm = source.getColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);
boolean alphaPremultiplied = cm.isAlphaPremultiplied();
target = new BufferedImage(cm, raster, alphaPremultiplied, null);
} else {
target = new BufferedImage(targetW, targetH, type);
}
Graphics2D g = target.createGraphics();
g.setBackground(new Color(255,255,255));
g.setColor(new Color(255,255,255));
g.fillRect(0, 0, targetW, targetH);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.drawImage(source.getScaledInstance(targetW, targetH, Image.SCALE_SMOOTH), 0, 0, null);
// g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
g.dispose();
return target;
}
/**
* 填充 word 模板(object数据格式)
*
* @param modelWordByte word模版二进制文件
* @param obj 要填充的数据
* @return 组合数据之后的word二进制
*/
public static byte[] fillWordDataByDomain(byte[] modelWordByte, Object obj) {
try {
Class<?> aClass = obj.getClass();
Field[] fields = aClass.getDeclaredFields();
Map<String, Object> data = new HashMap<>(fields.length);
for (Field field : fields) {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), aClass);
Method method = pd.getReadMethod();
String key = field.getName();
Object value = method.invoke(obj);
if (value != null) {
data.put(key, value);
} else {
data.put(key, " ");
}
}
return fillWordDataByMap(modelWordByte, data);
} catch (Exception e) {
e.printStackTrace();
return modelWordByte;
}
}
/**
* 填充 word 模板(map数据格式)
*
* @param file word二进制
* @param data 要填充的数据
* @return 组合数据之后的word二进制
*/
public static byte[] fillWordDataByMap(byte[] file, Map<String, Object> data) throws Exception {
byte[] ret = null;
if (data == null || data.isEmpty()) {
return ret;
}
long start = System.currentTimeMillis();
try (InputStream is = new ByteArrayInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Document doc = new Document(is);
DocumentBuilder builder = new DocumentBuilder(doc);
Map<String, String> toData = new HashMap<>();
for (Map.Entry<String, Object> en : data.entrySet()) {
String key = en.getKey();
Object value = en.getValue();
if (value instanceof List) {
//写入表数据
DataTable dataTable = fillListData((List) value, key, builder);
doc.getMailMerge().executeWithRegions(dataTable);
}
if (value instanceof BufferedImage) {
builder.moveToMergeField(key);
builder.insertImage((BufferedImage) value);
}
if (value instanceof byte[]) {
builder.moveToMergeField(key);
if ("signImage3".equals(key)){
builder.insertImage((byte[]) value,60,45);
} else {
builder.insertImage((byte[]) value,80,60);
}
}
//方框打勾
if (CODE_TICK.equals(value)){
builder.moveToMergeField(key);
//设置字体
builder.getFont().setName("Wingdings 2");
builder.write("\uF052");
}
//方框
if (CODE_BOX.equals(value)){
builder.moveToMergeField(key);
//设置字体
builder.getFont().setName("Wingdings 2");
builder.write("\uF0A3");
}
//为空则默认空字符串
String valueStr = String.valueOf(en.getValue());
if (value == null || "null".equals(value)) {
valueStr = " ";
}
toData.put(key, valueStr);
}
String[] fieldNames = new String[toData.size()];
String[] values = new String[toData.size()];
int i = 0;
for (Map.Entry<String, String> entry : toData.entrySet()) {
fieldNames[i] = entry.getKey();
values[i] = entry.getValue();
i++;
}
//合并数据
doc.getMailMerge().execute(fieldNames, values);
doc.save(out, SaveOptions.createSaveOptions(SaveFormat.DOCX));
ret = out.toByteArray();
}
long end = System.currentTimeMillis();
log.info("replace docx completed, elapsed " + (end - start) / 1000.0 + " seconds!");
return ret;
}
/**
* 封装 list 数据到 word 模板中(word表格)
*
* @param list 数据
* @param tableName 表格列表变量名称
* @return word表格数据DataTable
*/
private static DataTable fillListData(List<Object> list, String tableName, DocumentBuilder builder) throws Exception {
//创建DataTable,并绑定字段
DataTable dataTable = new DataTable(tableName);
for (Object obj : list) {
//创建DataRow,封装该行数据
DataRow dataRow = dataTable.newRow();
Class<?> objClass = obj.getClass();
Field[] fields = objClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
dataTable.getColumns().add(fields[i].getName());
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), objClass);
Method method = pd.getReadMethod();
dataRow.set(i, method.invoke(obj));
}
dataTable.getRows().add(dataRow);
}
return dataTable;
}
/*
* 加载 license
* 由于 aspose是收费的,若没有 license,则会出现水印。
*/
static {
try {
AsposeLicenseUtil.setWordsLicense();
} catch (Exception e) {
throw new RuntimeException("自动加载aspose证书文件失败!");
}
}
}
图片填充有两种方式:
一种使用BufferedImage 工具类转换图片插入对应的域;
一种是使用自带的图片插入方法 DocumentBuilder.insertImage(文件流,字节等,图片的参数宽,高);注:这种方式图片会更清晰。
模板替换还可以替换特殊字符等
//设置字体
builder.getFont().setName("Wingdings 2");
builder.write("\uF052");//打勾的方框 需要其他字符就找对应的编码就行