一、准备pom依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
二、准备sparePartsProcurePoCatalogTemplate.html
相对路径:templates\attrchs\sparePartsProcurePoCatalogTemplate.html
<!DOCTYPE html>
<html>
<meta charset='utf-8'>
<style type='text/css'>
body {
margin: 0;
}
.pdfdom {
/* width: 900px; */
background: #fff;
box-sizing: border-box;
}
.clearfix {
clear: both;
height: 0;
overflow: hidden;
align: center
}
.pdfdom .a4-container {
background: #fff;
border: 1px solid #ddd;
box-sizing: border-box;
position: relative;
}
.pdfdom .a4-container .delivery-title {
border-bottom: 2px solid #ddd;
box-sizing: border-box;
}
.pdfdom .a4-container .delivery-title img {
width: 100px;
box-sizing: border-box;
padding-left: 10px;
}
.pdfdom .a4-container .delivery-title .title {
margin: 0;
height: 88px;
line-height: 88px;
margin-left: 30px;
}
.pdfdom .a4-container .delivery-title .delivery-company-name {
float: left;
font-weight: bold;
line-height: 10px;
margin: 15px 0px 0px 25px;
}
.pdfdom .a4-container .delivery-title .allocate-code {
width: 200px;
line-height: 15px;
font-size: 12px;
float: left;
margin: 28px 0px 0px 8px;
}
.pdfdom .a4-container .main-content {
border-bottom: 1px solid #ddd;
}
.pdfdom .a4-container .main-content table tr th,
.pdfdom .a4-container .main-content table tr td {
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 10px;
font-size: 13px;
font-weight: 700;
box-sizing: border-box;
}
.pdfdom .a4-container .main-content table tr th:last-child,
.pdfdom .a4-container .main-content table tr td:last-child {
border-right: 0px solid #ddd;
}
.pdfdom .a4-container .test-container {
border-bottom: 1px solid #ddd;
display: flex;
}
.pdfdom .a4-container .test-container .test-container-table {
/* width: 100%; */
height: 300px;
}
.pdfdom .a4-container .test-container table tr th,
.pdfdom .a4-container .test-container table tr td {
border: 1px solid #ddd;
padding: 10px 4px;
font-size: 12px;
/* width: 16%; */
box-sizing: border-box;
}
#body-bottom table tr td {
font-size: 14px;
}
.pdfdom .a4-container .a4-remarks {
padding: 14px 10px;
line-height: 20px;
font-size: 16px;
border-bottom: 2px solid #ddd;
box-sizing: border-box;
}
.pdfdom .a4-container .a4-img-container {
padding: 14px 10px;
font-size: 20px;
border-bottom: 2px solid #ddd;
}
.pdfdom .a4-container .a4-img-container .img-box {
float: left;
width: 20%;
}
.pdfdom .a4-container .a4-img-container .img-box .img {
display: inline-block;
margin: 0 auto;
width: 125px;
height: 125px;
}
.pdfdom .a4-container .bottom-content {
box-sizing: border-box;
}
.pdfdom .a4-container .bottom-content table tr th,
.pdfdom .a4-container .bottom-content table tr td {
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
width: 50%;
padding: 10px;
font-size: 16px;
vertical-align: middle
}
.pdfdom .a4-container .bottom-content table tr th:last-child,
.pdfdom .a4-container .bottom-content table tr td:last-child {
border-right: 0px solid #ddd;
}
.pdfdom .a4-container .bottom-content .img {
height: 90px;
}
.green {
color: #14A16A;
}
.red {
color: #F14855;
}
.pdfdom .a4-container table {
width: 100%;
border-collapse: collapse;
}
.a4-remarks {
padding: 14px 10px;
line-height: 26px;
font-size: 16px;
border-bottom: 2px solid #ddd;
}
.a4-remarks .tc {
text-align: center;
}
.a4-remarks .indent2 {
text-indent: 2em;
}
.a4-remarks .indent3 {
text-indent: 3em;
}
.a4-remarks .lt {
margin-top: 15px;
color: #333333;
}
</style>
<body>
<div class='pdfdom'>
<div class='a4-container'>
<div class='delivery-title'>
<div style="float:left;">
<img style="width: 85px; height:85px;" src="data:image/png;base64,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">
</div>
<div class='delivery-company-name'>
<h3>采购订单</h3>
</div>
<div class='clearfix'></div>
</div>
<div class='main-content'>
<table>
<tr>
<td>订单时间:${orderTime!}</td>
<td>订单编号:${orderCode!}</td>
</tr>
<tr>
<td>需方(甲方):${partyAName!}</td>
<td>供方(乙方):${partyBName!}</td>
</tr>
<tr>
<td>授权代表:${demandAgent!}</td>
<td>授权代表:${supplierAgent!}</td>
</tr>
<tr>
<td>地址:${demandAddress!}</td>
<td>地址:${supplierAddress!}</td>
</tr>
<tr>
<td>联系方式:${demandContactsStyle!}</td>
<td>联系方式:${supplierContactsStyle!}</td>
</tr>
<tr>
<td colspan="2">经双方友好协议确认,根据下列条款订立本合同:</td>
</tr>
<tr>
<td colspan="2">一、内容:(甲方向乙方订购以下货物):</td>
</tr>
</table>
</div>
<div class='test-container'>
<table cellspacing="0" class="test-container-table">
<tr>
<th width="50px">序号</th>
<th width="100px">零配件SKU名称</th>
<th width="100px">零配件SKU编码</th>
<th width="100px">适用车型</th>
<th width="100px">品牌</th>
<th width="100px">零配件型号</th>
<th width="80px">单位</th>
<th width="80px">数量</th>
<th width="100px">含税单价(元)</th>
<th width="100px">含税金额(元)</th>
<th width="80px">收货人</th>
<th width="120px">收货人联系方式</th>
<th width="100px">收货地址</th>
<th width="100px">预估交期</th>
</tr>
<#list a as detail >
<tr>
<td width="50px">${detail.sequence}</td>
<td width="100px">${detail.skuName}</td>
<td width="100px">${detail.skuCode}</td>
<td width="100px">${detail.brandModelName}</td>
<td width="100px">${detail.brandName}</td>
<td width="100px">${detail.modelName}</td>
<td width="80px">${detail.productUnitName}</td>
<td width="80px">${detail.purchaseQuantity}</td>
<td width="100px">${detail.unitPriceIncludeTax}</td>
<td width="100px">${detail.includeTotalAmount}</td>
<td width="80px">${detail.receivedPerson}</td>
<td width="120px">${detail.receivedPhone}</td>
<td width="100px">${detail.receivedAddress}</td>
<td width="100px">${detail.estimateTime}</td>
</tr>
</#list>
<tr>
<td colspan="2">货款合计(大写):(含税费及运费)</td>
<td colspan="6">${totalAmountStr!}</td>
<td colspan="1">小写合计</td>
<td colspan="5">${totalAmount!}</td>
</tr>
<tr>
<td colspan="2">备注</td>
<td colspan="12">${data.remark}</td>
</tr>
</table>
</div>
<div id="body-bottom" class='test-container'>
<table cellspacing="0" class="test-container-table">
<tr>
<td colspan="2">二、本订单未约定事宜以关联采购合同编号: ${data.contractCode!}为准。</td>
</tr>
<tr>
<td colspan="2">三、其他约定</td>
</tr>
<tr>
<td colspan="2">1)收货允差天数:正三负零,即提前三天范围内交货是可以接受的,禁止晚交货。</td>
</tr>
<tr>
<td colspan="2">2)供方在收到订单后,请确认品名规格、数量、交期并于1个工作日内签回。</td>
</tr>
<tr>
<td colspan="2">3)本订单未约定的内容以框架合同为准;本订单约定的内容与框架合同不一致的,以本订单为准。</td>
</tr>
<tr>
<td colspan="2">4)本订单中的物料明细不在关联框架合同和补充协议的《供应商产品价格确认表》中的,关于该物料的相关约束使用本订单关联框架合同所有的条款约定 。</td>
</tr>
<tr>
<td colspan="2">5)本合同正本一式贰份,甲乙双方各执壹份,经双方盖章生效;甲方通过指定邮箱向乙方下达的订单与原合同具有同等效力。</td>
</tr>
<tr>
<td align="center">甲方</td>
<td align="center">乙方</td>
</tr>
<tr>
<td>甲 方(盖章): ${partyAName!}</td>
<td>乙 方(盖章): ${partyBName!}</td>
</tr>
<tr>
<td>授权代表(签字):</td>
<td>授权代表(签字):</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>
三、准备转换代码
/**
* 生成 PDF 文件
*
* @param procurePoDTO
*/
public void generatePdfFile(SparePartsProcurePoDTO procurePoDTO,SSOUser ssoUser) {
this.generatePoCatalogPdfToOss(procurePoDTO,ssoUser);
}
/**
* @Description 生成pdf到阿里云
* @Param procurePoDTO
* @Return void
* @Date 2023/10/20 10:24
* @Author lwp
*/
public String generatePoCatalogPdfToOss(SparePartsProcurePoDTO procurePoDTO,SSOUser ssoUser) {
SparePartsProcurePo sparePartsProcurePo = procurePoDTO.getSparePartsProcurePo();
String orderCode = sparePartsProcurePo.getOrderCode();
String htmlStr = this.getHtmlFromPoCatalog(procurePoDTO);
String url = "";
//渲染pdf
File file = null;
String name = AliOSSUtil.generateUUIDName(".pdf");
try {
File fileDir = new File(System.getProperty("user.dir") + "/pdf/");
if (!fileDir.exists()) {
fileDir.mkdirs();
}
file = new File(fileDir.getPath() + "/" + name);
file.createNewFile();
log.info("采购订单:{} 创建临时文件 {} 是否创建成功:{}", orderCode, file.getPath(), file.exists());
} catch (IOException e) {
log.error("采购订单:{} PDF生成失败", orderCode, e);
}
try (FileOutputStream fos = new FileOutputStream(file)) {
PdfSupport.convertStringToPdf(htmlStr, fos);
log.info("采购订单:{} 上传文件 {} 到阿里云OSS,文件大小 {} byte", orderCode, file.getName(), file.length());
url = aliossClient.uploadFile(file, "/procurePo/basic/pdf/"+name);
log.info("采购订单:{} PDFURL:{} 上传到阿里云OSS成功", orderCode, url);
if (Objects.nonNull(ssoUser)) {
uploadExcel(AliOSSHelper.getAccessUrl(url), "易维&四川泰瑞达新能源汽车有限公司-采购订单", ssoUser);
}
log.info("采购订单:{} PDFURL:{} 数据库更新成功", orderCode, url);
} catch (IOException e) {
log.error("采购订单:{} PDF生成失败", orderCode, e);
} finally {
if (file.exists()) {
file.delete();
}
}
return url;
}
/**
* @Description 解析生成html格式
* @Param procurePoDTO
* @Return java.lang.String
* @Date 2023/10/20 10:25
* @Author lwp
*/
public String getHtmlFromPoCatalog(SparePartsProcurePoDTO procurePoDTO) {
SpareParsProcurePoCatalogTemplateData data = buildData(procurePoDTO);
Map<String, Object> params = new HashMap<>();
params.put("orderTime",data.getOrderTime());// 订单时间
params.put("orderCode", data.getOrderCode());// 订单编号
params.put("partyAName", data.getPartyAName());// 需方(甲方)
params.put("partyBName", data.getPartyBName());// 供方(乙方)
params.put("demandAgent", data.getDemandAgent());// 甲方授权代表
params.put("supplierAgent", data.getSupplierAgent());// 乙方授权代表
params.put("demandAddress", data.getDemandAddress());// 甲方地址
params.put("supplierAddress", data.getSupplierAddress());// 乙方地址
params.put("demandContactsStyle", data.getDemandContactsStyle());// 甲方联系方式
params.put("supplierContactsStyle", data.getSupplierContactsStyle());// 乙方联系方式
params.put("totalAmountStr", data.getTotalAmountStr());// 货款合计(大写):(含税费及运费)
params.put("totalPurchaseQuantity", data.getTotalPurchaseQuantity());// 总数量
params.put("totalAmount", data.getTotalAmount());// 小写合计
params.put("contractCode", data.getContractCode());// 合同编码
params.put("paymentMethodName",data.getPaymentMethodName()); //结算方式
params.put("invoiceTypeName",data.getInvoiceTypeName()); //发票类型
params.put("a", data.getDetails());// 采购订单明细列表
return FreemarkerHelper.renderHtml("attrchs/sparePartsProcurePoCatalogTemplate.html", params);
}
/**
* 上传到导出记录
* @param url
* @param fileName
*/
public void uploadExcel(String url, String fileName, SSOUser ssoUser){
Date date = new Date();
ExportRecord exportRecord = new ExportRecord();
exportRecord.setFileUrl(url);
exportRecord.setFileName(fileName);
exportRecord.setModule(10);
exportRecord.setModuleName("零配件采购订单");
//处理完成
exportRecord.setStatus(2);
exportRecord.setSource(0);
exportRecord.setFileClassification(1);
exportRecord.setRequestMethod("GET");
exportRecord.setServerName("dst-operate-basic-api");
exportRecord.setFileType(FileTypeConstant.PDF);
exportRecord.setCreateUserId(ssoUser.getId());
exportRecord.setCreateUserName(ssoUser.getRealname());
exportRecord.setCreateTime(date);
exportRecord.setUpdateUserId(ssoUser.getId());
exportRecord.setUpdateUserName(ssoUser.getRealname());
exportRecord.setUpdateTime(date);
exportRecordService.save(exportRecord);
}
四、转换工具 FreemarkerHelper
package com.dst.common.service;
import freemarker.cache.URLTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class FreemarkerHelper {
private static Configuration config;
static {
config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
config.setClassicCompatible(true);
config.setLocalizedLookup(false);
config.setDateFormat("yyyy-MM-dd");
config.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
config.setDefaultEncoding("UTF-8");
}
public static String renderHtml(String tplName, Map<String,Object> dataModel){
String htmlStr = "";
try {
config.setClassLoaderForTemplateLoading(FreemarkerHelper.class.getClassLoader(),"/templates/");
Template tpl = config.getTemplate(tplName);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
tpl.process(dataModel,writer);
htmlStr = stringWriter.toString();
} catch (IOException | TemplateException e) {
log.error("【renderHtml】处理html异常:",e);
}
return htmlStr;
}
public static String renderHtmlFromUrl(String tplUrl, Map<String,Object> dataModel){
String htmlStr = "";
try {
config.setTemplateLoader(new RemoteTemplateLoader());
Template tpl = config.getTemplate(tplUrl);
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
tpl.process(dataModel,writer);
htmlStr = stringWriter.toString();
} catch (IOException | TemplateException e) {
log.error("【renderHtmlFromUrl】处理html异常:",e);
}
return htmlStr;
}
static class RemoteTemplateLoader extends URLTemplateLoader{
@Override
protected URL getURL(String name) {
URL url = null;
try {
url = new URL(name);
} catch (MalformedURLException e) {
log.error("【getURL】获取URL异常:",e);
}
return url;
}
}
public static void main(String[] args) throws Exception {
/*URL url = new URL("http://dst2.oss-cn-shenzhen.aliyuncs.com/test_contract_template_af3404a51225458da3ae5a587076f1cf");
URLConnection connection = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
reader.lines().forEach(line -> System.out.println(line));*/
Map<String,Object> dataModel = new HashMap<>();
dataModel.put("myDate",new Date());
String result = renderHtml("test.html",dataModel);
System.out.println(result);
// System.out.println(InetAddress.getByName("dst2.oss-cn-shenzhen.aliyuncs.com").getHostAddress());
}
}
五、HTML转PDF工具类PdfSupport
package com.dst.common.service;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.attach.ITagWorkerFactory;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.font.FontProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
public abstract class PdfSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(PdfSupport.class);
public static FontProgram simsun;
public static FontProgram awesome;
public static ImageData logo;
public static FontProvider fontProvider;
public static FontProvider createFontProvider() {
if (fontProvider == null){
fontProvider = new FontProvider();
fontProvider.addStandardPdfFonts();
fontProvider.addFont(simsun);
fontProvider.addFont(awesome);
}
return fontProvider;
}
protected static ConverterProperties createConverterProperties(){
ConverterProperties prop = new ConverterProperties();
prop.setFontProvider(createFontProvider());
// prop.setBaseUri("/static");
return prop;
}
public static void convertToPdf(String htmlStr, OutputStream pdfStream, ConverterProperties converterProperties) throws IOException {
HtmlConverter.convertToPdf(htmlStr,pdfStream,converterProperties);
}
public static void convertStringToPdf(String htmlStr, OutputStream pdfStream, ITagWorkerFactory tagWorkerFactory) throws IOException {
ConverterProperties converterProperties = createConverterProperties();
converterProperties.setTagWorkerFactory(tagWorkerFactory);
HtmlConverter.convertToPdf(htmlStr,pdfStream,converterProperties);
}
public static void convertStringToPdf(String htmlStr, OutputStream pdfStream) throws IOException {
HtmlConverter.convertToPdf(htmlStr,pdfStream,createConverterProperties());
}
public static PdfDocument createPdfDocument(OutputStream outputStream) throws Exception {
return new PdfDocument(new PdfWriter(outputStream));
}
public static Document convertToDocument(String html, PdfDocument pdfDoc, ConverterProperties prop) throws IOException {
return HtmlConverter.convertToDocument(html,pdfDoc,prop);
}
public void outFile(String fileName, String htmlStr,HttpServletResponse response) {
try {
String value = "attachment;filename=" + new String((fileName + System.currentTimeMillis() + ".pdf").getBytes("gb2312"), "ISO8859-1");
response.setHeader("Content-Disposition", value);
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");//设置头
response.setDateHeader("Expires", 0);//设置日期头
OutputStream out = response.getOutputStream();
convertToPdf(htmlStr,out);
out.flush();
} catch (Exception e) {
LOGGER.error("OutputStream error", e);
}
}
protected abstract void convertToPdf(String htmlStr, OutputStream out) throws Exception;
}
六、转换实体类
package com.dstcar.entitys.sparepartsprocurepo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpareParsProcurePoCatalogTemplateData implements Serializable {
/** 采购订单号 规则:CGDD+日期+流水号,按日期累加 **/
private String orderCode;
/** 订单日期 **/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private String orderTime;
/** 甲方名称 **/
private String partyAName;
/** 乙方名称 **/
private String partyBName;
/** 需方授权代表 **/
private String demandAgent;
/** 供方授权代表 **/
private String supplierAgent;
/** 需方地址 **/
private String demandAddress;
/** 供方地址 **/
private String supplierAddress;
/** 需方联系方式 **/
private String demandContactsStyle;
/** 供方联系方式 **/
private String supplierContactsStyle;
/** 结算方式:1月结30天|2月结60天|3月结90天|4先款后货(字典:spare_parts_payment_method) **/
private String paymentMethodName;
/** 发票类型:1增值税发票2普通发票(字典:spare_parts_po_invoice_type) **/
private String invoiceTypeName;
/** 订单总额(产品明细含税总额合计【含税总额(含税单价unitPriceIncludeTax * 采购数量purchaseQuantity)】) **/
private BigDecimal totalAmount;
/**
* 订单总额大写文字
*/
private String totalAmountStr;
/**
* 总的采购数量
*/
private BigDecimal totalPurchaseQuantity;
/** 备注 **/
private String remark;
/** 关联合同号 **/
private String contractCode;
/** po订单明细 */
private List<SpareParsProcurePoCatalogSkuTemplateData> details;
}
package com.dstcar.entitys.sparepartsprocurepo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpareParsProcurePoCatalogSkuTemplateData implements Serializable {
/**
* 序号
*/
private Integer sequence;
/** 零配件sku名称 **/
private String skuName;
/** 零部件SKU编码 **/
private String skuCode;
/**
* 适用车型名称
* 备注:适用车型下拉框选择‘通用’时该字段为空
* 适用车型下拉框选择‘部分车型适用’时该字段翻译(适用车型里面的品牌+型号拼接而成)
*/
private String brandModelName;
/**
* 零配件品牌名称
*/
private String brandName;
/**
* 零配件型号名称
*/
private String modelName;
/** 计量单位 */
private String productUnitName;
/** 采购数量 **/
private BigDecimal purchaseQuantity;
/** 含税单价 **/
private BigDecimal unitPriceIncludeTax;
/** 含税总额(含税单价*采购数量) **/
private BigDecimal includeTotalAmount;
/** 收货人 **/
private String receivedPerson;
/** 收货人联系电话 **/
private String receivedPhone;
/** 收货人地址 **/
private String receivedAddress;
/** 预估交期 **/
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private String estimateTime;
}