詹学伟
詹学伟
Published on 2024-04-21 / 22 Visits
0
0

itext之html模板生成pdf

一.根据html模板生成pdf文档

  • 说明:使用html模板,这里采用的是freemarker,ognl表达式,然后使用itextpdf读取模板,并动态填充业务数据到html模板中,最后将html转成pdf输出。

  • 注意:

    • 1.html模板编写中,存在部分样式丢失,如绝对定位相对定位等等。

    • 2.该方式暂时没有直接获取对应输出的pdf总页码功能,需要用到另外一个工具。

    • 3.htm模板中如果存在动态表格,使用ognl表达式进行遍历ide会提示报错(实际没问题)

    • 4.动态填充的数据字段不能为空,否则程序也会报错。因此在填充动态数据之前,所有的字段都需要空判断。

实现步骤

1.添加依赖

<!--pdf预览添加依赖-->
<!-- 页面渲染模板 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/ognl/ognl -->
<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>3.2.10</version>
</dependency>

<!-- pdf -->
<!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>2.1.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.itextpdf/barcodes -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>barcodes</artifactId>
    <version>7.1.7</version>
    <exclusions>
        <exclusion>
            <artifactId>io</artifactId>
            <groupId>com.itextpdf</groupId>
        </exclusion>
        <exclusion>
            <artifactId>kernel</artifactId>
            <groupId>com.itextpdf</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 获取pdf页码 -->
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.24</version>
</dependency>

2.下载字体和制作模板

  • 字体文件:1713678481715.jpg

  • html模板文件:

<!DOCTYPE html>
<html lang="en">
 <head>
  <style>
   /* common */
   table {
    border-spacing: 0;
    border-color: transparent;
    width: 100%;
    border-width: 1px;
    border: none;
    border-collapse: collapse;
   }

   tr {
    width: 100%;
   }

   th {
    padding: 10px;
    border: 1px solid #c8c8c8;
    font-size: 14px;
   }

   td {
    padding: 8px;
    padding-left: 24px;
    border: 1px solid #c8c8c8;
    font-size: 13px;

   }

   * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
   }

   .bg-head-1 {
    background-color: #2986f1;
    color: #ffffff;
   }

   .bg-head-2 {
    background-color: #e9e9e9;
   }

   .color-3b3b3b{
    color: #3B3B3B;
   }

   .content {
    width: 904px;
    margin: 0 auto;
    background-size: 100%;
    position: relative;
    min-height: 100vh;
   }

   /* header */
   .head-box {
    width: 904px;
    height: 107px;
    margin: 0 auto;
    margin-top: 20px;
   }

   .head-top {
    width: 904px;
    height: 100px;
    margin: 0 auto;
   }

   .head-left {
    width: 779px;
    height: 100px;
    line-height: 100px;
    text-align: center;
    float: left;
   }

   .title {
    font-size: 24px;
    color: #2986f1;
    font-family: "Microsoft YaHei UI";
    font-weight: 800;
    margin-left: 120px;
   }

   .head-right {
    width: 120px;
    height: 100px;
    float: left;
   }

   .qrcode-box {
    width: 120px;
    height: 80px;
    text-align: center;
   }

   .qrcode-img {
    width: 80px;
    height: 80px;
   }

   .qrcode-img_ {
    width: 280px;
    height: 132PX;
   }

   .qrcode-txt-box {
    width: 120px;
    height: 20px;
    text-align: center;
   }

   .txt {
    font-size: 13px;
    color: #959595;
   }

   .head-bottom {
    width: 904px;
    height: 7px;
    margin: 0 auto;
    margin-top: 10px;
   }

   .separate-left {
    width: 270px;
    height: 7px;
    background-color: #d0d0d0;
    transform: skewX(-45deg);
    float: left;
   }

   .separate-right {
    width: 634px;
    height: 7px;
    background-color: #2986f1;
    transform: skewX(-45deg);
    float: left;
   }


   /* 基本信息 */
   .base-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   /* 主要总成和系统的主要零部件种类范围 */
   .main-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   .easy-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   .special-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   .power-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   .insurance-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   .dispute-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
   }

   .footer-box {
    width: 904px;
    height: auto;
    margin: 0 auto;
    margin-top: 20px;
    text-indent: 2em;
    margin-bottom: 20px;
    font-size: 13px;
   }
  </style>
 </head>
 <body>
  <div class="content">
   <!-- 头部 -->
   <div class="head-box">
    <div class="head-top">
     <div class="head-left">
      <span class="title">平行进口汽车三包凭证预览</span>
     </div>
     <div class="head-right">
      <div class="qrcode-box">
       <img class="qrcode-img"
        src="${vehicleQrCodeImage}"
        alt="">
      </div>
      <div class="qrcode-txt-box">
       <span class="txt">电子三包凭证</span>
      </div>
     </div>
    </div>
    <div class="head-bottom">
     <!-- 分隔盒子 -->
     <div class="separate-left"></div>
     <div class="separate-right"></div>
    </div>
   </div>

   <!-- 基本信息 -->
   <div class="base-box">
    <table border="1">
     <tr class="bg-head-1">
      <th colspan="2">
       <span>基本信息</span>
      </th>
     </tr>
     <tr>
      <td colspan="2">
       <span>三包凭证编号:${threeGuaranteesBasic.vin}</span>
      </td>
     </tr>

     <tr>
      <th colspan="2" class="bg-head-2">
       <span class="color-3b3b3b">产品信息</span>
      </th>
     </tr>
     <tr>
      <td>
       <span>产品品牌:${threeGuaranteesBasic.certificationModelBrand}</span>
      </td>
      <td>
       <span>型号:${threeGuaranteesBasic.certificationModelNumber}</span>
      </td>
     </tr>
     <tr>
      <td style="border-bottom: none;">
       <span>车辆类型:${threeGuaranteesBasic.vehicleType}</span>
      </td>
      <td style="border-bottom: none;">
       <span>生产日期:${threeGuaranteesBasic.productionDate}</span>
      </td>
     </tr>
    </table>

    <table border="1">
     <tr>
      <th colspan="2" class="bg-head-2">
       <span class="color-3b3b3b">生产者信息</span>
      </th>
     </tr>
     <tr>
      <td style="min-width: 450px;">
       <span>名称:${threeGuaranteesBasic.proCompanyName}</span>
      </td>
      <td>
       <span>邮政编码:${threeGuaranteesBasic.proPostalCode}</span>
      </td>
     </tr>
     <tr>
      <td>
       <span>地址:${threeGuaranteesBasic.proBusinessAddress}</span>
      </td>
      <td>
       <span>客服电话:${threeGuaranteesBasic.proCompanyPhone}</span>
      </td>
     </tr>


     <tr>
      <th colspan="2" class="bg-head-2">
       <span class="color-3b3b3b">销售者信息</span>
      </th>
     </tr>
     <tr>
      <td>
       <span>名称:${threeGuaranteesBasic.saleCompanyName}</span>
      </td>
      <td>
       <span>邮政编码:${threeGuaranteesBasic.salePostalCode}</span>
      </td>
     </tr>
     <tr>
      <td style="border-bottom: none;">
       <span>地址:${threeGuaranteesBasic.saleBusinessAddress}</span>
      </td>
      <td style="border-bottom: none;">
       <span>客服电话:${threeGuaranteesBasic.saleCompanyPhone}</span>
      </td>
     </tr>
    </table>

    <table border="1">
     <tr>
      <th colspan="2" class="bg-head-2">
       <span class="color-3b3b3b">修理者信息</span>
      </th>
     </tr>
     <tr>
      <td colspan="2">
       <span>平行进口汽车三包保修网点信息查询:</span>
      </td>
     </tr>
     <tr>
      <td style="border-right: none;border-bottom:none;max-width: 500px;">
       <div style="line-height: 30px;">
        <span>方式一:中国汽车三包网:${threeGuaranteesBasic.threeGuaranteeNet}</span><br>
        <span>方式二:平行进口汽车三包服务网:${threeGuaranteesBasic.threeGuaranteeServiceNet}</span><br>
        <span>方式三:微信扫一扫右侧二维码</span>
       </div>
      </td>
      <td style="border-left: none;border-bottom:none;margin: 0%;padding: 0%;text-align: right;">
       <div style="width: auto;height: 164px;padding: 16px 10px 16px 0px;">
        <img class="qrcode-img_"
         src="${threeGuaranteesBasic.threeGuaranteeServiceQrCode}"
         alt="">
       </div>
      </td>
     </tr>
    </table>

    <table border="1">
     <tr>
      <th colspan="2" class="bg-head-2">
       <span class="color-3b3b3b">交付信息</span>
      </th>
     </tr>
     <tr>
      <td style="border-bottom: none;">
       <span>开具购车发票日期:${threeGuaranteesBasic.openInvoiceDate}</span>
      </td>
      <td style="border-bottom: none;">
       <span>交付车辆日期:${threeGuaranteesBasic.deliverDate}</span>
      </td>
     </tr>
    </table>


    <table border="1">
     <tr>
      <th colspan="2" class="bg-head-2">
       <span class="color-3b3b3b">三包条款</span>
      </th>
     </tr>
     <tr>
      <td colspan="2">
       <span>汽车产品包修期:${qualityGuaranteeTermHtmlVo.guaranteePeriod}</span>
      </td>
     </tr>
     <tr>
      <td colspan="2">
       <span>汽车产品三包有效期:${qualityGuaranteeTermHtmlVo.threeGuaranteesValidPeriod}</span>
      </td>
     </tr>
     <tr>
      <td colspan="2">
       <span>退换车补偿系数及计算公式:${qualityGuaranteeTermHtmlVo.returnExchangeDescription}</span>
      </td>
     </tr>
     <tr>
      <td style="min-width: 200px;">
       <div style="">
        <span>其他三包责任承诺:</span>
       </div>
      </td>
      <td style="">
       <p style="font-size: 13px;line-height: 20px;">
        ${qualityGuaranteeTermHtmlVo.otherThreeGuaranteesPromise}
       </p>
      </td>
     </tr>
     <tr>
      <td colspan="2">
       销售者签章:
      </td>
     </tr>
    </table>
   </div>

   <!-- 主要总成和系统的主要零部件种类范围 -->
   <div class="main-box">
    <table border="1" style="text-align: center;">
     <tr class="bg-head-1">
      <th colspan="2">
       <span>主要总成和系统的主要零部件种类范围</span>
      </th>
     </tr>
     <tr>
      <th style="min-width: 200px;">
       <span class="color-3b3b3b">总成系统</span>
      </th>
      <th>
       <span class="color-3b3b3b">主要零部件种类范围</span>
      </th>
     </tr>
     <#if qualityGuaranteeTermHtmlVo.mainVo?? && (qualityGuaranteeTermHtmlVo.mainVo?size > 0) >
      <#list qualityGuaranteeTermHtmlVo.mainVo as item>
       <tr>
        <td>
         ${item.partKindSystem}
        </td>
        <td style="text-align: left;margin-left: 24px;">
         <#assign index = 0>
         <#if item.partKindRanges?exists>
          <#list item.partKindRanges as obj>
           <#if index != 0>,</#if>
           ${obj?trim}
           <#assign index = index+1>
          </#list>
         </#if>
        </td>
       </tr>
      </#list>
     </#if>
    </table>
   </div>

   <!-- 易损耗零部件种类范围及质量保证期 -->
   <div class="easy-box">
    <table border="1" style="text-align: center;">
     <tr class="bg-head-1">
      <th colspan="4">
       <span>易损耗零部件种类范围及质量保证期</span>
       <span style="font-size: 12px;">(易损件零部件种类范围以国标GB/T29632-2021为准)</span>
      </th>
     </tr>
     <tr>
      <th>
       <span class="color-3b3b3b">易损耗零部件</span>
      </th>
      <th>
       <span class="color-3b3b3b">质量保证期(以先到者为准)</span>
      </th>
      <th>
       <span class="color-3b3b3b">易损耗零部件</span>
      </th>
      <th>
       <span class="color-3b3b3b">质量保证期(以先到者为准)</span>
      </th>
     </tr>
     <#if qualityGuaranteeTermHtmlVo.easyVos?? && (qualityGuaranteeTermHtmlVo.easyVos?size > 0) >
      <#list qualityGuaranteeTermHtmlVo.easyVos as item>
       <tr>
        <td>
         ${item.k1}
        </td>
        <td>
         ${item.v1}
        </td>
        <td>
         ${item.k2}
        </td>
        <td>
         ${item.v2}
        </td>
       </tr>
      </#list>
     </#if>

    </table>
   </div>

   <!-- 特殊零部件种类范围 -->
   <div class="special-box">
    <table border="1">
     <tr class="bg-head-1">
      <th>
       <span>特殊零部件种类范围</span>
      </th>
     </tr>
     <tr>
      <th>
       <span class="color-3b3b3b">需要根据车辆识别代码(VIN)等定制的特殊零部件种类范围</span>
      </th>
     </tr>
     <tr>
      <td>
       <#assign index = 0>
       <#if qualityGuaranteeTermHtmlVo.special?exists>
        <#list qualityGuaranteeTermHtmlVo.special as item>
         <#if index != 0>,</#if>
         ${item?trim}
         <#assign index = index+1>
        </#list>
       </#if>
      </td>
     </tr>

    </table>
   </div>

   <!-- 动力蓄电池容量衰减限值 -->
   <div class="power-box">
    <table border="1" style="text-align: center;">
     <tr class="bg-head-1">
      <th colspan="2">
       <span>动力蓄电池容量衰减限值</span>
      </th>
     </tr>
     <tr>
      <th>
       <span class="color-3b3b3b">需要根据车辆识别代码(VIN)等定制的特殊零部件种类范围</span>
      </th>
      <th>
       <span class="color-3b3b3b">容量衰减限值</span>
      </th>
     </tr>
     <#if qualityGuaranteeTermHtmlVo.batteryLossCustomVo?? && (qualityGuaranteeTermHtmlVo.batteryLossCustomVo?size > 0) >
      <#list qualityGuaranteeTermHtmlVo.batteryLossCustomVo as item>
       <tr>
        <td style="text-align: left;margin-left: 24px;">
         ${item.period}
        </td>
        <td>
         ${item.attenuationLimit}
        </td>
       </tr>
      </#list>
     </#if>

     <tr>
      <td colspan="2" style="text-align: left;margin-left: 24px;">
       <span>注:正常使用情况下,动力蓄电池Ah容量较额定容量的衰减不超过此表。</span>
      </td>

     </tr>

    </table>
   </div>

   <!-- 平行进口汽车三包保险信息 -->
   <div class="insurance-box">
    <table border="1" style="text-align: center;">
     <tr class="bg-head-1">
      <th colspan="4">
       <span>平行进口汽车三包保险信息</span>
      </th>
     </tr>
     <tr>
      <th>
       <span class="color-3b3b3b">保险公司名称</span>
      </th>
      <th>
       <span class="color-3b3b3b">保险产品名称</span>
      </th>
      <th>
       <span class="color-3b3b3b">保单号</span>
      </th>
      <th>
       <span class="color-3b3b3b">客服电话</span>
      </th>
     </tr>
     <tr>
      <td>
       <span>${threeGuaranteesPolicy.companyName}</span>
      </td>
      <td>
       <span>${threeGuaranteesPolicy.insuranceType}</span>
      </td>
      <td>
       <span>${threeGuaranteesPolicy.policyNo}</span>
      </td>
      <td>
       <span>${threeGuaranteesPolicy.insureHotLine}</span>
      </td>
     </tr>

    </table>
   </div>

   <!-- 三包责任争议处理 -->
   <div class="dispute-box">
    <table border="1" style="text-align: center;">
     <tr class="bg-head-1">
      <th colspan="3">
       <span>三包责任争议处理</span>
      </th>
     </tr>
     <tr>
      <th>
       <span class="color-3b3b3b">三包责任争议处理机构</span>
      </th>
      <th>
       <span class="color-3b3b3b">地址</span>
      </th>
      <th>
       <span class="color-3b3b3b">咨询电话</span>
      </th>
     </tr>

     <#if disputeInstitutions?? && (disputeInstitutions?size > 0) >
      <#list disputeInstitutions as item>
      <tr>
       <td>
        ${item.name}
       </td>
       <td>
        ${item.address}
       </td>
       <td>
        ${item.phone}
       </td>
      </tr>
      </#list>
     </#if>

    </table>
   </div>

   <div class="footer-box">
    ${promptOne}
   </div>
  </div>
 </body>
</html>

3.效果图

1713678516419.jpg

1713678508255.jpg

4.代码实现

  • 1.工具类

package com.yiliancar.process.third.utils;

import com.itextpdf.barcodes.Barcode128;
import com.itextpdf.barcodes.BarcodeQRCode;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.ITagWorker;
import com.itextpdf.html2pdf.attach.ProcessorContext;
import com.itextpdf.html2pdf.attach.impl.DefaultTagWorkerFactory;
import com.itextpdf.html2pdf.attach.impl.tags.HtmlTagWorker;
import com.itextpdf.html2pdf.attach.impl.tags.ImgTagWorker;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.IPropertyContainer;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.styledxmlparser.node.IElementNode;
import com.itextpdf.styledxmlparser.node.impl.jsoup.node.JsoupElementNode;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.FileCopyUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;

/**
 * @author zhanxuewei
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PdfUtil {

    private static final ConverterProperties CONVERTER_PROPERTIES;
    private static final PageSize DEFAULT_LABEL_L10 = new PageSize(new Rectangle(0F, 0F, 775.00F, 1000.00F));
    private static final PageSize A4 = PageSize.A4;

    public static final String BARCODE_TYPE_BARCODE128 = "barcode128";
    public static final String BARCODE_TYPE_QRBARCODE = "qrBarcode";
    public static final String BARCODE_CODE_NUMBER_VALUE = "code";

    static {
        CONVERTER_PROPERTIES = new ConverterProperties();
        CONVERTER_PROPERTIES.setTagWorkerFactory(new CustomTagWorkerFactory());
        CONVERTER_PROPERTIES.setCharset("UTF-8");
        FontProvider fontProvider = new FontProvider();
        fontProvider.addStandardPdfFonts();

        addFont(fontProvider, "pdf/simsun.ttf");
        addFont(fontProvider, "pdf/simsunb.ttf");
        addFont(fontProvider, "pdf/code128.ttf");
        addFont(fontProvider, "pdf/simhei.ttf");
        addFont(fontProvider, "pdf/GenJyuuGothicX-Medium.ttf");
        addFont(fontProvider, "pdf/Microsoft_YaHei_UI_Bold.ttf");
        addFont(fontProvider, "pdf/NotoSansCJKsc-Regular.otf");
    }

    /**
     * Add font {@link FontProvider}
     * @param fontProvider {@link FontProvider}
     * @param fontFileName fontFileName
     */
    private static void addFont(FontProvider fontProvider, String fontFileName) {
        try (InputStream resourceAsStream = PdfUtil.class.getClassLoader().getResourceAsStream(fontFileName)) {
            byte[] bytes = FileCopyUtils.copyToByteArray(resourceAsStream);
            fontProvider.addFont(bytes, PdfEncodings.IDENTITY_H);
            CONVERTER_PROPERTIES.setFontProvider(fontProvider);
        } catch (IOException e) {
            log.warn("读取字体文件出错:" + e.getMessage());
        }
    }

    public static ByteArrayInputStream bytes2InputStream(byte[] bytes) {
        return new ByteArrayInputStream(bytes);
    }

    public static byte[] merge(List<InputStream> inputStreams) throws IOException {
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            PdfDocument newPdf = new PdfDocument(new PdfWriter(out));
            for (InputStream inputStream : inputStreams) {
                Objects.requireNonNull(inputStream);
                PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputStream));
                pdfDocument.copyPagesTo(1, pdfDocument.getNumberOfPages(), newPdf);
            }
            newPdf.close();
            return out.toByteArray();
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            throw new RuntimeException("合并标签失败,系统内部错误. ", ex);
        }
    }

    public static InputStream html2Pdf(String html) throws IOException {
        return html2Pdf(html, CONVERTER_PROPERTIES, DEFAULT_LABEL_L10);
    }

    public static InputStream html2PdfA4(String html) throws IOException {
        return html2Pdf(html, CONVERTER_PROPERTIES, A4);
    }

    public static InputStream html2Pdf(String html, ConverterProperties converterProperties, PageSize pageSize) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PdfWriter pdfWriter = new PdfWriter(out);
        PdfDocument pdfDocument = new PdfDocument(pdfWriter);
        pdfDocument.setDefaultPageSize(pageSize);
        HtmlConverter.convertToPdf(html, pdfDocument, converterProperties);
        return new ByteArrayInputStream(out.toByteArray());
    }

}

class CustomHtmlTagWorker extends HtmlTagWorker {
    public CustomHtmlTagWorker(IElementNode element, ProcessorContext context) {
        super(element, context);
        Document document = (Document) getElementResult();
        document.setLeftMargin(1F);
        document.setRightMargin(1F);
        document.setTopMargin(1F);
        document.setBottomMargin(1F);
    }

    public static CustomHtmlTagWorker newInstance(IElementNode element, ProcessorContext context) {
        return new CustomHtmlTagWorker(element, context);
    }
}

@Slf4j
class CustomTagWorkerFactory extends DefaultTagWorkerFactory {
    @Override
    public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
        if (tag instanceof JsoupElementNode) {
            JsoupElementNode tag1 = (JsoupElementNode) tag;
            String name = tag1.name();
            if ("html".equals(name)) {
                return CustomHtmlTagWorker.newInstance(tag, context);
            }
        }
        if ("img".equals(tag.name())) {
            String codeType = tag.getAttribute("codeType");
            log.info("getAttribute:{}", codeType);
            if (PdfUtil.BARCODE_TYPE_QRBARCODE.equals(codeType)) {
                return new QRCodeTagWorker(tag, context);
            }
            if (PdfUtil.BARCODE_TYPE_BARCODE128.equals(codeType)) {
                return new Barcode128Work(tag, context);
            }
        }
        return super.getCustomTagWorker(tag, context);
    }
}

class Barcode128Work extends ImgTagWorker {
    Image image;

    /**
     * Creates a new {@link ImgTagWorker} instance.
     *
     * @param element the element
     * @param context the context
     */
    public Barcode128Work(IElementNode element, ProcessorContext context) {
        super(element, context);
    }

    @Override
    public void processEnd(IElementNode element, ProcessorContext context) {
        Barcode128 barcode128 = new Barcode128(context.getPdfDocument());
        String code = element.getAttribute(PdfUtil.BARCODE_CODE_NUMBER_VALUE);
        barcode128.setCode(code);
        PdfFormXObject formObject = barcode128.createFormXObject(context.getPdfDocument());
        image = new Image(formObject);
    }

    @Override
    public IPropertyContainer getElementResult() {
        return image;
    }
}

@Slf4j
class QRCodeTagWorker extends ImgTagWorker {
    private BarcodeQRCode qrCode;
    private Image qrCodeAsImage;

    public QRCodeTagWorker(IElementNode element, ProcessorContext context) {
        super(element, context);
    }

    @Override
    public void processEnd(IElementNode element, ProcessorContext context) {
        String code = element.getAttribute(PdfUtil.BARCODE_CODE_NUMBER_VALUE);
        qrCode = new BarcodeQRCode(code);
        //Transform barcode into image
        qrCodeAsImage = new Image(qrCode.createFormXObject(context.getPdfDocument()));

    }

    @Override
    public IPropertyContainer getElementResult() {
        return qrCodeAsImage;
    }
}

  • 核心业务代码

private final FreeMakerHelper freeMakerHelper;
String html = freeMakerHelper.process("certificate.html", threePackagesCredentialsHtmlVO);
InputStream inputStream = PdfUtil.html2Pdf(html);

二、根据pdf模板生成


Comment