最近项目中,涉及到票据导出功能,excel的导出功能已不能满足当前需求,在考虑到对开发人员减负,因此考虑使用Freemarker模块渲染HTML,在导出PDF方式的实现。
有了上面的考虑,必须知道如何实现,因此网上查询有多个处理方案,例如jsPDF、IText、wkhtmltopdf等方式,由于其中每个方案都有优缺点,综合自身的项目成员考虑,选用Itext来实现,因为HTML编写对项目成员要求不高,而且无效安装客户端等等。
<!-- 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>
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
*/
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;
}
}
@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);
}
function exportPdf(data) {
window.open('/invoicedetail/htmltopdf?id=' + data.id,'target','');
}
本来打算将模板文件放到网盘,考虑代码篇幅过大,但是临时编写,所以先贴在这里,希望大家不会因为太多代码而反感:模板名称为:template_freemarker_invoice.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>