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>
View Code

 

posted @ 2024-08-27 11:25  xzlnuli  阅读(18)  评论(0编辑  收藏  举报