背景:需要将数据填充到一个模板里面,根据这个模板生成pdf文件或者数据流。首先考虑的是AdobeAcrobat DC生成一个pdf模板,然后填充数据,但是考虑框中填充表格数据时,可能不满足动态数据撑开输入框,所以改用Velocity模板动态生成html转为PDF文件或者数据流
1.POM文件
<!-- html转PDF -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.1.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.1.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>2.0.1</version>
</dependency>
2.准备Velocity模板
用法和Velocity模板使用方式是一致,附一个简单的模板样例
如果表格用<tfoot>,表格的每页都会显示底行,但是如果需要只是尾页显示表格底行,可以采用一个<tr>即可
<div class="sku-list">
<table cellpadding="0" cellspacing="0" width="100%">
<colgroup>
<col width="48"/>
<col width="147"/>
<col width="147"/>
<col width="147"/>
<col width="147"/>
<col width="100"/>
<col width="100"/>
<col width="100"/>
</colgroup>
<thead>
<tr>
<th class="left">$!printText.table.index</th>
<th></th>
<th class="left">$!printText.table.title1</th>
<th>$!printText.table.title2</th>
<th>$!printText.table.title3</th>
<th>$!printText.table.title4</th>
<th>$!printText.table.title5</th>
<th>$!printText.table.title6</th>
</tr>
</thead>
<tbody>
#if($!printInfo.data)
#foreach ($!print in $!printInfo.data)
#set($index = $foreach.count)
<tr>
<td class="left">$!{index}</td>
<td class="left">
<img src="$!print.imgPath" width="60" height="60"/>
</td>
<td class="left">$!print.name</td>
<td>$!print.id</td>
<td>$!print.content1</td>
<td>$print.content2</td>
<td>$!print.content3</td>
<td>$!print.content4</td>
</tr>
#end
#end
</tbody>
<tr class="foot">
<td></td>
<td></td>
<td></td>
<td>$!printText.table.total</td>
<td></td>
<td>$!printInfo.contentOne</td>
<td>$!printInfo.contentTwo</td>
<td>$!printInfo.contentThree</td>
</tr>
</table>
</div>
3.准备多语言
yaml需要的包
<dependency>
<groupId>org.jyaml</groupId>
<artifactId>jyaml</artifactId>
<version>1.3</version>
</dependency>
多语言的方式有很多,这里使用的是yaml方式做的多语言,通过Locale
取的语言取出对应的文案
en_US:
table:
index: No.
title1: t1
title2: t2
title3: t3
title4: t4
title5: t5
title6: t6
total: Total
zh_CN:
table:
index: No.
title1: 标题1
title2: 标题2
title3: 标题3
title4: 标题4
title5: 标题5
title6: 标题6
total: Total
4.准备Velocity引擎工具
public class VelocityUtils {
/**
* 初始化velocity引擎
*
* @param path 模板所在路径
*/
public static void initVelocityEngine(String path) {
velocityEngine = new VelocityEngine();
velocityEngine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, path);// 这是模板所在路径
velocityEngine.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
//指定编码格式,避免生成模板就造成乱码,影响到转pdf后的文件
velocityEngine.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
velocityEngine.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
velocityEngine.init();
}
/**
* 得到模板引擎渲染数据后的写入器
* @param templatePath 模板所在位置
* @param paramMap 模板数据k-y
* @return
*/
public static Writer getWriterByTemplate(String templatePath, Map paramMap) {
Writer writer =new StringWriter();
Template template = velocityEngine.getTemplate(templatePath, "utf-8");
VelocityContext context = new VelocityContext(paramMap);
template.merge(context, writer);
return writer;
}
}
5.测试demo
测试生成html,查看样式是否满足要求,进行微调
-
准备基本数据
//获取yaml多语言资源 File file = new File( "/对应yaml文件的根路径/xxxx.yml"); Map map = (Map) Yaml.load(file); String localStr = "en_US"; //打印的数据源 PrintInfo printInfo = new PrintInfo(); Map paramValue = new ConcurrentHashMap(); paramValue.put("printInfo",printInfo); paramValue.put("printText",map.get(localStr));
-
初始化引擎
String path = "src/main/webapp/WEB-INF/vm"; VelocityUtils.initVelocityEngine(path);
-
生成html文件进行微调
OutputStream os = new FileOutputStream("/utils/test.html"); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = in.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); }
生成pdf文件,可以使用
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(new FileOutputStream("/utils/test.pdf")));
-
生成base64字符串
//生成字节输出流 ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); //放入pdf文本 PdfDocument pdfDocument = new PdfDocument(new PdfWriter(swapStream)); //主要转化工具 html -> pdf HtmlConverter.convertToPdf(in,pdfDocument); //base64加密器 Base64.Encoder encoder = Base64.getEncoder(); //加密为base64字符串 String base64 = encoder.encodeToString(swapStream.toByteArray());
6.最终方法
/**
* 获取pdf的base64字符串
*
* @param templatePath 模板路径
* @param paramValue html入参的参数
* @return base64字符串
* @throws Exception
*/
protected String getPDF2Base64Str(String templatePath, Map paramValue) throws Exception {
//输入流
Writer writer = VelocityUtils.getWriterByTemplate(templatePath, paramValue);
InputStream in = new ByteArrayInputStream(writer.toString().getBytes(Charset.defaultCharset()));
//字节输出流
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(swapStream));
//html -> pdf
HtmlConverter.convertToPdf(in, pdfDocument);
//转化为base64字符串
return Base64.getEncoder().encodeToString(swapStream.toByteArray());
}
如果需要根据base64字符串查看图片或文件什么样,可以用<iframe>
,src用data:application/pdf;base64,
+bae64字符串组合作为值,即可查看图片或文件,也可以直接data:application/pdf;base64,
+bae64字符串组合放到浏览器上即可预览图片信息