项目需求: 为订单后生成对应的pdf文档,文档内包含图片。
方案一:使用freemarker和itext把html转pdf,存在中文乱码,宋体不识别,需下载simsun.ttc字体,空白占位符不识别等等问题,对前端依赖性较大,不推荐;
方案二:freemarker生成word文档后通过微软提供的openoffice工具转pdf文档;
方案三: freemarker生成word文档后通过aspose.words转pdf文档,该方式不需要安装插件,只需导入对应jar包就好,但生成pdf存在水印,需要进行破解;(aspose.words需要付费的哦,用于商业请授权购买正版)
方案四:python用pdfkit生成pdf,各位有时间自己研究吧;
先来看看效果图
进入正题
1.生成pdf文档流程
1):准备一个word表格,然后另存为xml格式的文件;
2):为了便于观看文档结构和编辑代码,对xml文件进行格式化(在线格式化网址);
3):把xml文件的后缀名改为ftl,使用freemarker表达式进行数据的动态添加(模板中图片是以base64编码展示的,替换成自己的图片编码);
4):导入相关jar包;
5):通过freemarker模板引擎技术把ftl模板文件转为word文档;
6):通过aspose.words把word文档转为pdf文档,至此完成;
2.相关jar包准备
<!--freemarker jar-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.22</version>
</dependency>
<!--二维码操作jar-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.aspose/aspose-words -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>14.7.0</version>
</dependency>
因为我们用的破解版,所以请点击此处下载我给各位准备的aspose-words.jar和ftl模板( 提取码: jrs5)
2.开始我们的代码
文档生成工具类Xml2Word2Pdf.java
import com.aspose.words.Document;
import com.aspose.words.SaveFormat;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Encoder;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
* @Author: zzr-zhangs
* @Date: 2020-01-14 13:12
* @Content: xml->word->pdf (通过freemarker把通过word生成的xml模板动态添加数据并生成word文档,通过aspose.words把word文档转为pdf文档)
* @Description: aspose.words需付费购买,目前使用破解版,需要引入freemarker的jar和aspose-words-jdk16-16.4.0.jar
*/
public class Xml2Word2Pdf {
private static Configuration configuration = null;
/**
* 初始化配置并设置默认编码UTF-8
*/
static {
configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
}
/**
* 通过模板文件创建word文档(模板文件的格式可到网站进行在线格式化,网址推荐:https://tool.oschina.net/codeformat/xml/)
*
* @param tpmplateFilePath 模板文件路径(完整路径,不包含文件如:D:/templates)
* @param tpmplateFileName 模板文件名称
* @param outFilePath 输出文件路径(完整路径,包含文件名称 如:D:/templates/order.doc)
* @param dataMap 需要动态添加的数据
*/
public static void createWord(String tpmplateFilePath, String tpmplateFileName, String outFilePath, Map<String, Object> dataMap) {
try {
//如果不传模板文件路径就默认取resources下的templates文件夹中的模板文件
if (StringUtils.isEmpty(tpmplateFilePath)) {
configuration.setClassForTemplateLoading(Xml2Word2Pdf.class, "/templates");
} else {
configuration.setDirectoryForTemplateLoading(new File(tpmplateFilePath)); // XML文件所存在的位置
}
//获取文档XML模板
Template template = configuration.getTemplate(tpmplateFileName);
//设置输出文件位置和文件名
File outFile = new File(outFilePath);
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
template.process(dataMap, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 验证aspose.word组件是否授权:无授权的文件有水印标记
*/
public static boolean getLicense() {
boolean result = false;
try {
String s = "<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>";
ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes());
//InputStream inputStream = Xml2Word2Pdf.class.getClassLoader().getResourceAsStream("\\license.xml");
com.aspose.words.License license = new com.aspose.words.License();
license.setLicense(inputStream);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 使用aspose.word把word文档转为pdf文档
*
* @param sourceFile word文档绝对路径(如:D:/templates/order.doc)
* @param destFile pdf文档绝对路径(如:D:/templates/order.pdf)
*/
public static String word2Pdf(String sourceFile, String destFile) throws Exception {
destFile = StringUtils.isEmpty(destFile) ? sourceFile.replace(".doc", ".pdf") : destFile;
// 验证License 若不验证则转化出的pdf文档会有水印产生
if (!getLicense()) {
throw new Exception("生成PDF文档,验证License失败!");
}
try {
File file = new File(destFile); //新建一个空白pdf文档
FileOutputStream os = new FileOutputStream(file);
Document doc = new Document(sourceFile);//通过sourceFile创建word文档对象
doc.save(os, SaveFormat.PDF);
os.close();
} catch (Exception e) {
e.printStackTrace();
throw new Exception("生成PDF文档失败!");
}
return destFile;
}
/**
* 图片转base64编码
*
* @param imgFilePath 图片路径
* @return 返回base64编码字符串
*/
public static String ima2Base64(String imgFilePath) {
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(imgFilePath);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
/**
* 简单的测试
*
* @remark 需要在D盘创建templates文件夹并准备相应的的xml和图片
*/
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
//1.基础数据定义
String templatePath = "D:/templates/";//模板路径/生成文档存放路径
String sourceFile = templatePath+"order.doc";//生成的word文档路径和名称
String templateName = "word_2003.ftl";//模板名称
String sealImgPath = "D:/templates/liangzai.png";//盖章图片路径
String nameImgPath = "D:/templates/zhangjie.jpg";//名字图片路径
//2. dataMap的赋值
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("orderNo", "ZRP202001150004");
dataMap.put("sealImgBase64", ima2Base64(sealImgPath));
dataMap.put("nameImgBase64", ima2Base64(nameImgPath));
//生成word文档
createWord(templatePath, templateName, sourceFile, dataMap);
//word文档转pdf文档
word2Pdf(sourceFile, null);
long end = System.currentTimeMillis();
System.out.println("文档生成耗时:" + ((end - start) / 1000.0) + "秒");
}
}
参考博客:https://blog.csdn.net/qq_41507845/article/details/90518846