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>

這裏寫圖片描述
掃描關注:全棧工程師成長記
一個可以交流的平臺,目的是爲了做一個影響最有影響力的人的平臺。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章