引言
最近項目中,涉及到票據導出功能,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>
掃描關注:全棧工程師成長記
一個可以交流的平臺,目的是爲了做一個影響最有影響力的人的平臺。