根据Velocity模板动态导出多语言PDF文件或流

背景:需要将数据填充到一个模板里面,根据这个模板生成pdf文件或者数据流。首先考虑的是AdobeAcrobat DC生成一个pdf模板,然后填充数据,但是考虑框中填充表格数据时,可能不满足动态数据撑开输入框,所以改用Velocity模板动态生成html转为PDF文件或者数据流

1.POM文件

		
        <dependency>
            <groupId>com.itextpdfgroupId>
            <artifactId>kernelartifactId>
            <version>7.1.1version>
        dependency>

        <dependency>
            <groupId>com.itextpdfgroupId>
            <artifactId>layoutartifactId>
            <version>7.1.1version>
        dependency>

        <dependency>
            <groupId>com.itextpdfgroupId>
            <artifactId>html2pdfartifactId>
            <version>2.0.1version>
        dependency>

2.准备Velocity模板

用法和Velocity模板使用方式是一致,附一个简单的模板样例
如果表格用,表格的每页都会显示底行,但是如果需要只是尾页显示表格底行,可以采用一个即可

<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.indexth>
                    <th>th>
                    <th class="left">$!printText.table.title1th>
                    <th>$!printText.table.title2th>
                    <th>$!printText.table.title3th>
                    <th>$!printText.table.title4th>
                    <th>$!printText.table.title5th>
                    <th>$!printText.table.title6th>
                  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.nametd>
                               	<td>$!print.idtd>
                                <td>$!print.content1td>
                                <td>$print.content2td>
                                <td>$!print.content3td>
                                <td>$!print.content4td>
                        	tr>
                       	#end
                   #end
              tbody>
              <tr class="foot">
                   <td>td>
                   <td>td>
                   <td>td>
                   <td>$!printText.table.totaltd>
                   <td>td>
                   <td>$!printInfo.contentOnetd>
                   <td>$!printInfo.contentTwotd>
                   <td>$!printInfo.contentThreetd>
              tr>
       table>
div>

3.准备多语言

yaml需要的包

		<dependency>
            <groupId>org.jyamlgroupId>
            <artifactId>jyamlartifactId>
            <version>1.3version>
        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,查看样式是否满足要求,进行微调

  1. 准备基本数据

    //获取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));
    
  2. 初始化引擎

     String path = "src/main/webapp/WEB-INF/vm";
     VelocityUtils.initVelocityEngine(path);
    
  3. 生成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")));

  4. 生成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字符串查看图片或文件什么样,可以用