版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/erlian1992/article/details/82631290
Java实现HTML(带图片)转PDF的解决方案
前端页面实现
后端具体实现:
Controller
package com.huajin.ptoms.controller.user;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.huajin.baymax.logger.XMsgError;
import com.huajin.baymax.logger.Xlogger;
import com.huajin.ptoms.controller.HjBaseController;
import com.huajin.ptoms.service.MemberFilingLetterService;
import com.huajin.ptoms.util.PdfUtil;
/**
* 会员资格证书控制层
*
* @author hongwei.lian
* @date 2018年9月10日 下午1:41:44
*/
@Controller
@RequestMapping("/user/membeFilingLetter")
public class MemberFilingLetterController extends HjBaseController {
@Autowired
private MemberFilingLetterService memberFilingLetterService;
/**
* 打印预览会员资格证书
*
* @param memberId
* @author hongwei.lian
* @date 2018年9月10日 下午1:41:58
*/
@RequestMapping("printletter")
public void printLetter(@RequestParam("memberId") int memberId){
try {
//-- 获取PDF签章地址
String filepath = memberFilingLetterService.downMemberLetter(memberId, super.getExchangeId());
//-- 设置PDF响应头
HttpServletResponse response = PdfUtil.setPdfResponse(super.response());
//-- 打印PDF
PdfUtil.printLetter(filepath, response.getOutputStream());
} catch (Exception e) {
Xlogger.error(XMsgError.buildSimple(getClass().getName(), "printLetter", e));
}
}
}
PdfUtil工具类
package com.huajin.ptoms.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.property.Property;
public class PdfUtil {
/**
* 设置PDF响应头
*
* @param response
* @return
* @author hongwei.lian
* @date 2018年9月10日 下午6:02:47
*/
public static HttpServletResponse setPdfResponse(HttpServletResponse response) {
response.setCharacterEncoding("utf-8");
response.setContentType("application/pdf");
return response;
}
/**
* 打印PDF
*
* @param filepath
* @param outputStream
* @author hongwei.lian
* @date 2018年9月10日 下午6:04:26
*/
public static void printLetter(String filepath, OutputStream outputStream){
FileInputStream inputStream = null;
try {
File file = new File(filepath);
inputStream = new FileInputStream(file);
byte[] data = new byte[(int)file.length()];
inputStream.read(data);
outputStream.write(data);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(outputStream)) {
outputStream.close();
}
if (Objects.nonNull(inputStream)) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
MemberFilingLetterService服务层
package com.huajin.ptoms.service;
import java.io.File;
import java.io.StringWriter;
import java.util.Date;
import java.util.Objects;
import org.apache.commons.io.FileUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.huajin.baymax.exception.BayMaxBaseException;
import com.huajin.baymax.support.ResponseCodeBase;
import com.huajin.baymax.util.DateUtils;
import com.huajin.exchange.enums.notice.LetterType;
import com.huajin.exchange.utils.PdfUtils;
import com.huajin.ptoms.domain.UcUser;
import com.huajin.usercenterclient.vo.SealApplyVo;
/**
* 会员资格证书生成业务层
*
* @author hongwei.lian
* @date 2018年9月10日 下午1:38:08
*/
@Service
public class MemberFilingLetterService {
@Autowired
private VelocityEngine velocityEngine;
@Autowired
private UcUserService ucUserService;
@Autowired
private SysParamService sysParamService;
@Autowired
private FeSealService feSealService;
@Autowired
private FeExchangeService feExchangeService;
/**
* 下载/打印会员资格证书
*
* @param memberId
* @param exchangeId
* @return
* @throws Exception
* @author hongwei.lian
* @date 2018年9月10日 下午1:38:52
*/
public String downMemberLetter(int memberId, int exchangeId) throws Exception {
UcUser member = ucUserService.getUserAllById(memberId, 0);
if (Objects.isNull(member)) {
throw new BayMaxBaseException(ResponseCodeBase.SYSTEM_ERROR, null, "会员不存在!");
}
//-- 创建目标文件目录
String destPath = sysParamService.getNoticeFileStorageAddress() + File.separator + "member" + File.separator;
File destPathFile = new File(destPath);
if (!destPathFile.exists()) {
destPathFile.mkdirs();
}
//-- 会员资格证书文件路径
String fileName = String.valueOf(LetterType.开户通知书.type) + "_" + member.getUserGuid() + ".pdf";
String noticeFilePath = destPath + fileName;
File destFile = new File(noticeFilePath);
if (!destFile.exists() || Objects.equals(FileUtils.sizeOf(destFile), 0)) {
String origin = destPath + "origin" + File.separator + fileName;
//-- 创建并生成pdf
this.createMemberNoticeFile(origin, member);
SealApplyVo seal = feSealService.getUserSeal(exchangeId);
if (Objects.nonNull(seal)) {
//-- 会员资格证书签章
feSealService.sealPdfByCoordinate(origin, destPath, seal.getSealInfoId(),
feExchangeService.getDefaultExchangeInfo().getExchangeName(), "会员资格证书", "会员资格证书",
1, 500, 50);
}
}
return noticeFilePath;
}
/**
* 生成会员资格证书
*
* @param destFilePath
* @param member
* @throws Exception
* @author hongwei.lian
* @date 2018年9月10日 下午1:39:11
*/
private void createMemberNoticeFile(String destFilePath, UcUser member) throws Exception {
VelocityContext context = new VelocityContext();
context.put("member", member);
context.put("exchange", feExchangeService.getExchangeInfo(member.getExchangeId()));
context.put("today", DateUtils.formatDate(new Date(), "yyyy年MM月dd日"));
StringWriter stringWriter = new StringWriter();
velocityEngine.mergeTemplate("/template/membership.html", "UTF-8", context, stringWriter);
PdfUtils.htmlToPdf(stringWriter.toString(), destFilePath);
}
}
PdfUtils工具类
package com.huajin.exchange.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.apache.logging.log4j.core.util.FileUtils;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
public class PdfUtils {
public static final void htmlToPdf(String htmlContent, String filePath) throws Exception {
FileUtils.mkdir(new File(filePath).getParentFile(), true);
try (OutputStream fileStream = new FileOutputStream(filePath)) {
ITextRenderer textRenderer = new ITextRenderer();
ITextFontResolver fontResolver = textRenderer.getFontResolver();
String agreementBody = htmlContent;
agreementBody = agreementBody.replace(" ", " ");
agreementBody = agreementBody.replace("–", "–");
agreementBody = agreementBody.replace("—", "—");
agreementBody = agreementBody.replace("‘", "‘"); // left single quotation mark
agreementBody = agreementBody.replace("’", "’"); // right single quotation mark
agreementBody = agreementBody.replace("‚", "‚"); // single low-9 quotation mark
agreementBody = agreementBody.replace("“", "“"); // left double quotation mark
agreementBody = agreementBody.replace("”", "”"); // right double quotation mark
agreementBody = agreementBody.replace("„", "„"); // double low-9 quotation mark
agreementBody = agreementBody.replace("′", "′"); // minutes
agreementBody = agreementBody.replace("″", "″"); // seconds
agreementBody = agreementBody.replace("‹", "‹"); // single left angle quotation
agreementBody = agreementBody.replace("›", "›"); // single right angle quotation
agreementBody = agreementBody.replace("‾", "‾"); // overline
fontResolver.addFont("/fonts/simsun.ttc", com.itextpdf.kernel.pdf.PdfName.IdentityH.getValue(), false);
textRenderer.setDocumentFromString(agreementBody, null);
textRenderer.layout();
textRenderer.createPDF(fileStream, true);
}
}
}
membership.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>会员资格证书</title>
<style>
@page {
size: 10.42in 7.42in;
margin: 0;
content: counter(page);
width: 100%;
height: 100%;
} /*不能随意修改这里 hongwei.lian*/
* {
margin:0;
padding:0;
}
html, body {
font-family:'SimSun';
font-size:14pt;
width: 100%;
height: 100%;
/*background:url(/data/App/ptoms.cfaoe.local/logo-bg.jpg);*/
}/*宋五*/
.vip {
position: relative;
margin: 0 auto;
width: 1000px;
height: 711px;
/* font-family: '宋体'; */
background: url(/data/App/ptoms.cfaoe.local/vip_bg.jpg) center center no-repeat;
}
div.vip-item {
position: absolute;
height: 30px;
border: none;
font-size: 20px;
font-weight: bold;
color: #333;
}
.companyname {
padding: 0 4px;
top: 319px;
left: 365px;
width: 487px;
background: url(/data/App/ptoms.cfaoe.local/vip_input-bg1.jpg);
}
.vipnumber {
padding: 0 4px;
top: 450px;
left: 221px;
width: 277px;
background: url(/data/App/ptoms.cfaoe.local/vip_input-bg2.jpg);
}
.date {
padding: 0 4px;
top: 450px;
left: 608px;
width: 195px;
background: url(/data/App/ptoms.cfaoe.local/vip_input-bg3.jpg);
}
</style>
</head>
<body>
<div class="vip">
<div class="vip-item companyname">
${member.companyName}
</div>
<div class="vip-item vipnumber">
${member.UserCode}
</div>
<div class="vip-item date">
${today}
</div>
</div>
</body>
</html>
核心的东西就是上面标红的代码和HTMNL页面代码。
具体需要查看VelocityContext和VelocityEngine两个核心类使用和源码
生成效果:
HTML转PDF依赖:
gradle依赖:
//-- velocity
compile ('org.apache.velocity:velocity:1.7')
//-- html to pdf
compile ('com.itextpdf:kernel:7.0.1')
compile ('org.xhtmlrenderer:flying-saucer-core:9.1.1')
compile ('org.xhtmlrenderer:flying-saucer-pdf-itext5:9.1.1')
//-- about
compile ('com.itextpdf:pdfa:7.0.1')
compile ('com.itextpdf:hyph:7.0.1')
compile ('com.itextpdf:sign:7.0.1')
compile ('com.itextpdf:barcodes:7.0.1')
compile ('com.itextpdf:font-asian:7.0.1')