SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)

SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)

文章目录

  • SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)
    • 1. 前言
    • 2. 技术思路
    • 3. 实现过程
    • 4. 测试

1. 前言

近期在项目种遇到了实时生成复杂 PDF 的需求,经过一番调研和测试,最终选择了采用 Thymeleaf 和 iText7 来实现需求,本文将详细介绍实现过程。

2. 技术思路

  1. 通过 Thymeleaf 渲染生成需要的页面内容;
  2. 通过 iText7 html2pdf 库将 Thymeleaf 渲染的结果转换成 PDF;
  3. 将 PDF 内容写入到接口输出流中返回给前端浏览器展示;

3. 实现过程

  1. Maven 引入依赖;

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    
    <dependency>
        <groupId>com.itextpdfgroupId>
        <artifactId>html2pdfartifactId>
        <version>5.0.0version>
    dependency>
    
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    
    
    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.8.21version>
    dependency>
    
  2. 编写 Thymeleaf 模板 resources/templates/demo.html

    DOCTYPE html>
    <html lang="zh">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>PDF Demotitle>
    
        <style>
            body {
                padding-top: 50px;
                padding-left: 60px;
                padding-right: 60px;
                font-family: 'KaiTi', serif;
            }
    
            .title {
                color: red;
                text-align: center;
                margin-bottom: 50px;
            }
    
            table {
                width: 100%;
                border: 1px solid black;
                border-spacing: 0;
            }
    
            th {
                border: 1px solid black;
                background-color: rgb(128, 128, 128);
            }
    
            td {
                border: 1px solid black;
            }
        style>
    head>
    
    <body>
    <h1 class="title" th:text="${title}">h1>
    
    <table>
        <thead>
        <tr>
            <th>序号th>
            <th>姓名th>
            <th>年龄th>
            <th>性别th>
        tr>
        thead>
        <tbody th:each="student, studentStat : ${students}">
        <tr>
            <td th:text="${studentStat.count}">td>
            <td th:text="${student.name}">td>
            <td th:text="${student.age}">td>
            <td th:text="${student.sex}">td>
        tr>
        tbody>
    table>
    
    body>
    html>
    
  3. 添加中文字体资源 resources/fonts/simkai.ttf

  4. 编写 PDF 页码事件处理 handler/PageEventHandler

    package com.xiaoqqya.itextpdf.handler;
    
    import com.itextpdf.kernel.events.Event;
    import com.itextpdf.kernel.events.IEventHandler;
    import com.itextpdf.kernel.events.PdfDocumentEvent;
    import com.itextpdf.kernel.geom.Rectangle;
    import com.itextpdf.kernel.pdf.PdfDocument;
    import com.itextpdf.kernel.pdf.PdfPage;
    import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
    import com.itextpdf.layout.Canvas;
    import com.itextpdf.layout.element.Paragraph;
    import com.itextpdf.layout.properties.TextAlignment;
    
    /**
     * 页码事件处理.
     *
     * @author xiaoQQya
     * @since 2023/08/29
     */
    public class PageEventHandler implements IEventHandler {
    
        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
            PdfDocument document = documentEvent.getDocument();
            PdfPage page = documentEvent.getPage();
            Rectangle pageSize = page.getPageSize();
    
            PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);
            Canvas canvas = new Canvas(pdfCanvas, pageSize);
            float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
            float y = pageSize.getBottom() + 15;
            Paragraph paragraph = new Paragraph("-- " + document.getPageNumber(page) + " --")
                    .setFontSize(10);
            canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER);
            canvas.close();
        }
    }
    
  5. 编写 Student 实体类 model/domain/Student

    package com.xiaoqqya.itextpdf.model.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /**
     * 学生.
     *
     * @author xiaoQQya
     * @since 2023/08/29
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 、
         * 年龄
         */
        private Integer age;
    
        /**
         * 性别
         */
        private String sex;
    }
    
  6. 编写 Service service/PdfService 生成 PDF;

    package com.xiaoqqya.itextpdf.service.impl;
    
    import cn.hutool.core.io.resource.ResourceUtil;
    import com.itextpdf.html2pdf.ConverterProperties;
    import com.itextpdf.html2pdf.HtmlConverter;
    import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
    import com.itextpdf.io.font.FontProgramFactory;
    import com.itextpdf.kernel.events.PdfDocumentEvent;
    import com.itextpdf.kernel.geom.PageSize;
    import com.itextpdf.kernel.pdf.PdfDocument;
    import com.itextpdf.kernel.pdf.PdfWriter;
    import com.itextpdf.layout.font.FontProvider;
    import com.xiaoqqya.itextpdf.exception.CustomException;
    import com.xiaoqqya.itextpdf.handler.PageEventHandler;
    import com.xiaoqqya.itextpdf.model.domain.Student;
    import com.xiaoqqya.itextpdf.service.PdfService;
    import org.springframework.stereotype.Service;
    import org.thymeleaf.TemplateEngine;
    import org.thymeleaf.context.Context;
    
    import javax.annotation.Resource;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * PDF Service.
     *
     * @author xiaoQQya
     * @since 2023/08/29
     */
    @Service
    public class PdfServiceImpl implements PdfService {
    
        @Resource
        private TemplateEngine templateEngine;
    
        /**
         * 生成 PDF.
         *
         * @param outputStream 输出流
         */
        @Override
        public void generatePdf(OutputStream outputStream) {
            // 模拟数据
            List<Student> students = new ArrayList<>();
            students.add(Student.builder().name("小红").age(18).sex("女").build());
            students.add(Student.builder().name("小强").age(21).sex("男").build());
            students.add(Student.builder().name("熊大").age(19).sex("男").build());
    
            // 生成 Thymeleaf 上下文
            Context context = new Context();
            context.setVariable("title", "PDF Demo");
            context.setVariable("students", students);
            String demo = templateEngine.process("demo", context);
    
            // 生成 PDF, 并添加页码
            try (PdfWriter pdfWriter = new PdfWriter(outputStream); PdfDocument pdfDocument = new PdfDocument(pdfWriter)) {
                pdfDocument.setDefaultPageSize(PageSize.A4);
                pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, new PageEventHandler());
    
                ConverterProperties converterProperties = new ConverterProperties();
                FontProvider fontProvider = new DefaultFontProvider(true, true, false);
                fontProvider.addFont(FontProgramFactory.createFont(ResourceUtil.readBytes("fonts/simkai.ttf")));
                converterProperties.setFontProvider(fontProvider);
                HtmlConverter.convertToPdf(demo, pdfDocument, converterProperties);
            } catch (IOException e) {
                throw new CustomException(e.getMessage());
            }
        }
    }
    
  7. 编写 Controller controller/PdfController 返回给前端浏览器展示;

    package com.xiaoqqya.itextpdf.controller;
    
    import com.xiaoqqya.itextpdf.service.PdfService;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * PDF Controller.
     *
     * @author xiaoQQya
     * @since 2023/08/29
     */
    @RestController
    @RequestMapping(value = "/pdf")
    public class PdfController {
    
        @Resource
        private PdfService pdfService;
    
        /**
         * 生成 PDF.
         */
        @GetMapping
        public void generatePdf(HttpServletResponse response) throws IOException {
            pdfService.generatePdf(response.getOutputStream());
        }
    }
    

4. 测试

浏览器访问 http://localhost:8080/pdf 查看效果。

参考文章:

  • 使用itext7将HTML转为pdf · Issue #12 · ydq/blog (github.com);

你可能感兴趣的:(Java,spring,boot,pdf,后端,java)