java使用html生成pdf
目录
1,依赖jar包
<!--pdf依赖--> <dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.11</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.2</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.1.20</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf-itext5</artifactId> <version>9.1.20</version> </dependency>
特别说明:因为需要构造freemark所以需要spring-context-support上下文依赖
2、构造freemark对象
@Configuration public class FreemarkerConfig { @Bean public FreeMarkerConfigurer freeMarkerConfigurer() { FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer(); freeMarkerConfigurer.setTemplateLoaderPath("classpath:/static"); return freeMarkerConfigurer; } } -- 若无法设置,需要添加上下文依赖才可以设置模板位置 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.3.13</version> </dependency>
3、添加pdf生成工具类
import com.itextpdf.text.pdf.BaseFont; import freemarker.template.Template; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextRenderer; import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.util.Map; /** * @author xiaozhilei * * pdf下载工具 */ @Slf4j @Component public class PdfCreator { @Autowired private FreeMarkerConfigurer freeMarkerConfigurer; /** * 字体,需要去管网下载 */ private static final String FONT = "/static/simsun.ttc"; /** * freemarker渲染html * * @param data * @param htmlTmp * @return */ public String freeMarkerRender(Map<String, Object> data, String htmlTmp) { Writer out = new StringWriter(); try { Template template = freeMarkerConfigurer.getConfiguration().getTemplate(htmlTmp); template.setNumberFormat("0"); // 合并数据模型与模板 template.process(data, out); out.flush(); return out.toString(); } catch (Exception e) { log.error("pdf转换yichang.{}",e.getMessage()); e.printStackTrace(); } finally { try { out.close(); } catch (IOException ex) { ex.printStackTrace(); } } return null; } /** * 生成pdf文档 * * @param content * @param outputStream */ public static void createPdfOutPut(String content, OutputStream outputStream) { try { ITextRenderer render = new ITextRenderer(); ITextFontResolver fontResolver = render.getFontResolver(); fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 解析html生成pdf render.setDocumentFromString(content); //解决图片相对路径的问题 //String logoPaht = "file:///target/classess/main/resources/5.png"; //render.getSharedContext().setBaseURL(logoPaht); render.layout(); render.createPDF(outputStream); }catch (Exception e){ log.error("pdf获取字体异常.{}",e.getMessage()); e.printStackTrace(); } }
4、业务代码开发
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * @author xiaozhilei * * pdf生成服务 */ @Service @Slf4j public class PdfReportServiceImpl implements PdfReportService { /** * html模板文件,下载模板,位置已经由feemark进行指定了 */ private static final String HTML = "report_template.html"; /** * html模板文件,查看模板,位置已经由freemark进行指定了 */ private static final String HTML_VIEW = "report_view.html"; @Autowired private PdfCreator pdfCreator; @Override public void downloadPdf(Long reportId, HttpServletResponse response) { PdfReportDTO expReport = new PdfReportDTO(); expReport.setReportName("测试报告"); expReport.setReportNo("1234556789"); OutputStream fileOutStream = null; try { Map<String,Object> data = new HashMap(); data.put("expReport",expReport); String content = pdfCreator.freeMarkerRender(data,HTML); //生成报告名称 String uuid= UUID.randomUUID().toString().trim().replaceAll("-", ""); response.reset(); response.setCharacterEncoding("UTF-8"); response.addHeader("Content-Disposition", "attachment;filename="+new String(uuid+".pdf")); response.setContentType("application/pdf"); fileOutStream = response.getOutputStream(); //生成pdf文件输出流 pdfCreator.createPdfOutPut(content,fileOutStream); }catch (Exception e){ e.printStackTrace(); }finally { try { fileOutStream.flush(); fileOutStream.close(); }catch (Exception e){ e.printStackTrace(); } } } @Override public String getReportHtml(Long reportId) { PdfReportDTO expReport = new PdfReportDTO(); expReport.setReportName("测试报告"); expReport.setReportNo("1234556789"); Map<String,Object> data = new HashMap(); data.put("expReport",expReport); return pdfCreator.freeMarkerRender(data,HTML_VIEW); }
5、添加html模板
说明:这个模板前端协助开发放入指定位置即可
模板实例:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-family: SimSun; font-style: normal; font-weight: normal; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ""; content: none; } table { border-collapse: collapse; border-spacing: 0; } [role="button"], input[type="submit"], input[type="reset"], input[type="button"], button { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } /* Reset `button` and button-style `input` default styles */ input[type="submit"], input[type="reset"], input[type="button"], button { background: none; border: 0; color: inherit; /* cursor: default; */ font: inherit; line-height: normal; overflow: visible; padding: 0; outline: 0; user-select: none; -webkit-appearance: button; /* for input */ -webkit-user-select: none; /* for button */ -moz-user-select: none; -ms-user-select: none; } input::-moz-focus-inner, button::-moz-focus-inner { border: 0; padding: 0; } * { box-sizing: border-box; } input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; } input[type="number"] { -moz-appearance: textfield; } .report-main { width: 595pt; margin: auto; /* margin-top: 48px; */ padding: 40px; } .report-header { margin-bottom: 55px; } .report-header .logo { height: 20px; } .report-header .report-desc { height: 32px; font-weight: 500; display: inline-block; font-size: 10px; margin-left: 363px; } .report-header .report-desc .report-desc__no, .report-header .report-desc .report-desc__time { line-height: 14px; } .report-header .report-desc .report-desc__no { margin-top: 4px; } .report-title { font-weight: 600; font-size: 18px; line-height: 25px; text-align: center; } .basic-info { margin-top: 40px; } .basic-info h2 { font-weight: 600; font-size: 12px; line-height: 17px; } .basic-info .basic-info__main { margin-top: 8px; } .basic-info .basic-info__main .basic-info__main__item { font-weight: 500; font-size: 10px; line-height: 14px; display: inline-block; margin-top: 8px; width: 49%; } .common-title { margin-top: 40px; } .common-title h2 { font-weight: 600; font-size: 12px; line-height: 17px; } .common-summery-text-align thead th:nth-child(1) { text-align: left; vertical-align: middle; } .common-summery-text-align thead th:not(.common-summery-text-align thead th:nth-child(1)) { text-align: right; } .common-summery-text-align tbody td:nth-child(1) { text-align: left; vertical-align: middle; } .common-summery-text-align tbody td:not(.common-summery-text-align tbody td:nth-child(1)) { text-align: right; } .common-table--title-blod.common-summery-text-align thead th:nth-child(1) { text-align: left; } .common-table--title-blod.common-summery-text-align thead th:not(.common-summery-text-align thead th:nth-child(1)) { text-align: left; } .common-table--title-blod.common-summery-text-align tbody td:nth-child(1) { text-align: left; vertical-align: top; } .common-table--title-blod.common-summery-text-align tbody td:not(.common-summery-text-align tbody td:nth-child(1)) { text-align: left; } .common-subtitle { margin-top: 17px; } .common-subtitle h2 { font-weight: 600; font-size: 10px; line-height: 14px; } .common-table { border-collapse: collapse; width: 100%; margin-top: 9px; border-spacing: 0; } .common-table.common-table--title-blod thead th { font-weight: bold; } .common-table.common-table--no-title-bg thead { background: #ffffff; } .common-table thead { background: #f0f0f0; font-weight: 500; font-size: 10px; line-height: 18px; } .common-table thead th { padding: 7px 6px; } .common-table, tbody tr td { padding: 7px 6px; } .common-table.common-table--no-border th, .common-table.common-table--no-border td { border: 0; } .common-table th, .common-table td { border: 1px solid #d2d2d2; } .common-table, tbody tr { font-weight: 500; font-size: 10px; line-height: 18px; text-align: center; vertical-align: middle; } .common-table.common-table--title-blod, tbody tr { font-weight: 500; font-size: 10px; line-height: 18px; text-align: center; vertical-align: top; } .margin-t-25 { margin-top: 25px; } .text-align-r { text-align: right !important; } .text-align-c { text-align: center !important; } .text-align-l { text-align: left !important; } body { -webkit-text-size-adjust: none; } .desc{ font-size: 10px; margin-top: 8px; } .desc-item{ font-weight: bold; line-height: 14px; } .display-ib{ display: inline-block; } .margin-t-8{ margin-top: 8px; } .vertical-top { vertical-align: top; } .vertical-middle { vertical-align: middle; } /* .percent-circle-svg { width: 100%; height: 115px; position: relative; margin-top: 16px; } .percent-circle-svg .line-left { position: absolute; right: calc(50% + 70px); top: calc(50% - 38px); font-weight: 500; font-size: 12px; line-height: 17px; border-bottom: 1px solid #000; } .percent-circle-svg .line-right { position: absolute; left: calc(60%); top: -22%; font-weight: 500; font-size: 12px; line-height: 17px; border-bottom: 1px solid #000; } .percent-circle-svg-main { position: absolute; left: 50%; transform: translateX(-50%); } .percent-svg { transform-origin: 55px 55px; transform: rotate(-90deg); transition: stroke-dasharray 0.3s ease-in; } .broken-line-left { width: 30px; height: 1px; border-bottom: 1px solid #000; position: absolute; right: calc(50% + 43px); top: 25%; transform: rotateZ(-31deg); } .broken-line-right { width: 30px; height: 1px; border-bottom: 1px solid #000; position: absolute; left: calc(50% + 25px); top: 0; transform: rotateZ(-34deg); } .percent-circle-svg-total { position: absolute; left: 50%; top: 50%; transform: translateY(-50%) translateX(-50%); font-weight: bold; font-size: 12px; line-height: 14px; text-align: center; } .percent-circle-svg-total span { margin-top: 3px; } */ .gas-line-percent { height: 24px; font-weight: 500; font-size: 12px; margin-top: 16px; text-align: center; } .sale-gas-line { border-radius: 5px 0 0 5px; height: 24px; background: #d2d2d2; } .loss-gas-line { height: 24px; background: #f0f0f0; border-radius: 0 5px 5px 0; } .sale-gas, .loss-gas { height: 24px; line-height: 24px; vertical-align: top; } .total-gas { height: 24px; font-weight: 500; font-size: 12px; line-height: 24px; margin-top: 8px; text-align: center; } .display-inline { display: inline-block; } @page:left{ margin: 0cm; } @page:right{ margin: 0cm; } </style> </head> <body> <div class="diagnosis-report"> <div class="report-main"> <!-- 头部 --> <div class="report-desc"> <div class="report-desc__no">报告编号:${expReport.reportNo}</div> </div> </div> </div> </body> </html>