iText实现HTML页面导出PDF

引言

最近项目中,涉及到票据导出功能,excel的导出功能已不能满足当前需求,在考虑到对开发人员减负,因此考虑使用Freemarker模块渲染HTML,在导出PDF方式的实现。

实现效果

在这里插入图片描述

前期准备

有了上面的考虑,必须知道如何实现,因此网上查询有多个处理方案,例如jsPDF、IText、wkhtmltopdf等方式,由于其中每个方案都有优缺点,综合自身的项目成员考虑,选用Itext来实现,因为HTML编写对项目成员要求不高,而且无效安装客户端等等。

实现功能

1、需要的jar包

<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.11</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.itextpdf.tool/xmlworker -->
<dependency>
    <groupId>com.itextpdf.tool</groupId>
    <artifactId>xmlworker</artifactId>
    <version>5.5.11</version>
</dependency>

2、封装导出类

package com.huafa.core.util;

import com.huafa.core.codegenerator.utils.PathUtil;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;

/**
 * @Auther: chenyanwu
 * @Date: 2019/3/20 15:50
 * @Description:
 * @Version 1.0
 */
public class PDFUtil {

    private static final String FONT = "simhei.ttf";

    private static Configuration freemarkerCfg = null;

    static {
        freemarkerCfg =new Configuration();
        //freemarker的模板目录
        try {
            freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 将freemark渲染为html,转换成pdf
     * @param t 动态数据
     * @param
     * @param <T>
     */
    public static <T> void htmlToPdf(T t, String fileName, String template, HttpServletResponse resp) {
        // 渲染html内容
        String content = PDFUtil.freeMarkerRender(t, template);

        Document document = new Document();
        try {
            resp.setCharacterEncoding("UTF-8");
            resp.setHeader("content-Type", "application/pdf");
            resp.setHeader("Content-Disposition",
                    "inline;filename=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));
            // 建立书写器
            PdfWriter writer = PdfWriter.getInstance(document, resp.getOutputStream());
            document.open();
            XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
            fontImp.register(FONT);
            XMLWorkerHelper.getInstance().parseXHtml(writer, document,
                    new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"), fontImp);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } finally {
            document.close();
        }
    }

    /**
     * freemarker渲染html
     */
    public static <T> String freeMarkerRender(T data, String htmlTmp) {
        Writer out = new StringWriter();
        try {
            // 获取模板,并设置编码方式
            Template template = freemarkerCfg.getTemplate(htmlTmp);
            template.setEncoding("UTF-8");
            // 合并数据模型与模板,将合并后的数据和模板写入到流中,这里使用的字符流
            template.process(data, out);
            out.flush();
            return out.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

}

3、需要的jar包

@GetMapping("/htmltopdf")
@ResponseBody
public void htmlToPdf(HttpServletResponse resp, String id) {
    Map<String,Object> data = new HashMap();

    InvoiceDetail invoiceDetail = mapper.findInvoiceDetailById(id);

    String invoiceCustomId = invoiceDetail.getInvoiceCustomId();
    InvoiceCustom invoiceCustom = invoiceCustomMapper.findInvoiceCustomById(invoiceCustomId);
    data.put("customName", invoiceCustom.getName());
    data.put("customTaxid", invoiceCustom.getCustomTaxid());
    data.put("customContact", invoiceCustom.getCustomContact());
    data.put("customBankinfo", invoiceCustom.getCustomBankinfo());

    data.put("contractNumber", invoiceDetail.getContractNumber());
    data.put("contractAmount", invoiceDetail.getContractAmount());
    data.put("contractPeriod", invoiceDetail.getContractPeriod());
    data.put("hasPaid", invoiceDetail.getHasPaid());
    data.put("invoiceAmount", invoiceDetail.getInvoiceAmount());
    data.put("bankAndDate", invoiceDetail.getBankAndDate());

    Example example = new Example(InvoiceInfo.class);
    example.createCriteria().andEqualTo("invoiceDetailId", id);
    List<InvoiceInfo> list = infoMapper.selectByExample(example);
    BigDecimal sum = BigDecimal.ZERO;
    for(InvoiceInfo invoiceInfo: list) {
        sum = sum.add(invoiceInfo.getAmount());
    }
    data.put("sum", sum);
    data.put("infos", list);

    String html = "template_freemarker_invoice.html";
    PDFUtil.htmlToPdf(data, "测试", html, resp);
}

4、前端调用

function exportPdf(data) {
    window.open('/invoicedetail/htmltopdf?id=' + data.id,'target','');
}

5、模板文件

本来打算将模板文件放到网盘,考虑代码篇幅过大,但是临时编写,所以先贴在这里,希望大家不会因为太多代码而反感:模板名称为:template_freemarker_invoice.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"></meta>
    <title>文档标题</title>
    <style>
        body {
            font-family:SimHei;
            text-align:center;
        }

        table
        {
            border-collapse:collapse;
            border-spacing: 0;
            width: 100%;
        }

        tr {
            height: 50px;
        }

        .content td {
            border: 1px solid black;
        }

    </style>
</head>
<body>
<h1>
    开具增值税  专用  普通发票申请表
</h1>
<table borde=0>
    <tr>
        <td style="width: 100px;">
            TO:财务部
        </td>
        <td style="width: 80%;">
        </td>
        <td style="width: 80px;">
            日期:
        </td>
        <td style="width: 120px;">
        </td>
    </tr>
</table>
<table class='content'>
    <tr>
        <td rowspan="4">
            发票信息
        </td>
        <td>
            客户名称*(必填)
        </td>
        <td colspan="6">
            <#if customName??>
            ${customName}
        </#if>
        </td>
    </tr>
    <tr>
        <td>
            客户税号*(必填)
        </td>
        <td colspan="6">
            <#if customTaxid??>
            ${customTaxid}
        </#if>
        </td>
    </tr>
    <tr>
        <td>
            客户地址、电话
        </td>
        <td colspan="6">
            <#if customContact??>
            ${customContact}
        </#if>
        </td>
    </tr>
    <tr>
        <td>
            客户开户行及账号
        </td>
        <td colspan="6">
            <#if customBankinfo??>
            ${customBankinfo}
        </#if>
        </td>
    </tr>
    <tr>
        <td rowspan="3">
            合同执行情况
        </td>
        <td>
            合同号
        </td>
        <td colspan="3">
            <#if contractNumber??>
            ${contractNumber}
        </#if>
        </td>
        <td>
            合同总金额(含税)
        </td>
        <td colspan="2">
            <#if contractAmount??>
            ${contractAmount}元
        </#if>
        </td>
    </tr>
    <tr>
        <td>
            合同期限
        </td>
        <td colspan="3">
            <#if contractPeriod??>
            ${contractPeriod}
        </#if>
        </td>
        <td>
            是否已付款
        </td>
        <td colspan="2">
            <#if hasPaid = 0>
            否
            <#else></#if>
        </td>
    </tr>
    <tr>
        <td>
            本次开票金额(含税)
        </td>
        <td colspan="3">
            <#if invoiceAmount??>
            ${invoiceAmount}元
        </#if>
        </td>
        <td>
            付款银行及日期
        </td>
        <td colspan="2">
            <#if bankAndDate??>
            ${bankAndDate}
        </#if>
        </td>
    </tr>
    <tr>
        <td rowspan="7">
            开票信息
        </td>
        <td>
            应税劳务名称
        </td>
        <td>
            规格型号
        </td>
        <td>
            单位
        </td>
        <td>
            数量
        </td>
        <td>
            单价
        </td>
        <td>
            金额(含税)
        </td>
        <td>
            发票号
        </td>
    </tr>
    <#if (infos?? && infos?size>0) >
    <#list infos as info>
    <tr>
        <td>
            <#if info.name??>
            ${info.name}
        </#if>
        </td>
        <td>
            <#if bankAndDate??>
            ${info.specification}
        </#if>
        </td>
        <td>
            <#if info.unit??>
            ${info.unit}
        </#if>
        </td>
        <td>
            <#if info.quantity??>
            ${info.quantity}
        </#if>
        </td>
        <td>
            <#if info.price??>
            ${info.price}元
        </#if>
        </td>
        <td>
            <#if info.amount??>
            ${info.amount}元
        </#if>
        </td>
        <td>
            <#if info.invoiceNumber??>
            ${info.invoiceNumber}
        </#if>
        </td>
    </tr>
</#list>
</#if>

<#if (infos?size<5) >
<#list 1..5-infos?size as i>
<tr>
    <td>
    </td>
    <td>
    </td>
    <td>
    </td>
    <td>
    </td>
    <td>
    </td>
    <td>
    </td>
    <td>
    </td>
</tr>
</#list>
</#if>
<tr>
    <td colspan="5">
        合计
    </td>
    <td>
        ${sum}元
    </td>
    <td>
    </td>
</tr>
<tr>
    <td colspan="2">
        经办人:
    </td>
    <td colspan="3">
    </td>
    <td>
        部门负责人:
    </td>
    <td colspan="2">
    </td>
</tr>
<tr>
    <td colspan="8" style="text-align: left;font-weight: bold;">
        申请开票部门郑重承诺:上述开票信息经我部门认真审核并确认无误,如因上述开票信息提供错误而引发的退票问题及由此产生的影响,由我部门负责。
    </td>
</tr>
</table>
<table borde=0>
    <tr>
        <td style="width: 120px;">
            签收人:
        </td>
        <td style="width: 60%;">
        </td>
        <td style="width: 100px;">
            签收日期:
        </td>
        <td style="width: 10%;">
        </td>
        <td style="width: 10%;">
        </td>
    </tr>
</table>

</body>
</html>

扫描关注:码蚁在线
在这里插入图片描述
一个可以交流的平台,目的是为了做一个影响最有影响力的人的平台。
在这里插入图片描述

展开阅读全文

没有更多推荐了,返回首页