Spring Boot 使用Itext绘制并导出PDF

发布于:2025-05-13 ⋅ 阅读:(15) ⋅ 点赞:(0)

最终效果

在这里插入图片描述

其实可以加分页,但是没有那么精细的需求,所以我最后就没有加,有兴趣的可以尝试下。

项目依赖

<!--    Spring Boot 版本有点老    -->
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>

<!--    依赖    -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13</version>
</dependency>

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>
        

实现代码

出奇的简单,就一个核心的方法

核心实现方法

package com.an.pdfhandle.service;

import com.an.pdfhandle.entity.Member;
import com.an.pdfhandle.entity.Team;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

import java.io.OutputStream;
import java.net.URL;
import java.util.List;

public class PdfExportService {


    // 样式化导出为类似画布的横向排版:iText5 实现核心逻辑(支持多个团队)

    public void exportTeamCanvasStyle(List<Team> teams, OutputStream outputStream) throws Exception {
        Document document = new Document(PageSize.A4, 36, 36, 36, 36); // A4 纵向页面
        PdfWriter.getInstance(document, outputStream);
        document.open();

        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        Font nameFont = new Font(baseFont, 12);
        Font labelFont = new Font(baseFont, 12, Font.BOLD);
        Font titleFont = new Font(baseFont, 16, Font.BOLD);

        for (Team team : teams) {
            // --- 信息页(第一页) ---
            document.newPage();
            document.add(new Paragraph("参 赛 队:" + team.getName(), titleFont));
            document.add(Chunk.NEWLINE);
            // 绘制基本代表队基本信息
            Paragraph p1 = new Paragraph();
            p1.setFont(nameFont);
            p1.add("领   队:" + team.getLeaderName() + "          联系电话:" + team.getLeaderPhone() + "\n");
            p1.add("主训教练:" + team.getCoachName() + "           队    医:" + team.getDoctorName() + "\n");
            p1.add("助理教练:" + team.getAssistantCoachName() + "           联系电话:" + team.getCoachPhone());
            document.add(p1);

            document.add(Chunk.NEWLINE);
            // 绘制Table
            PdfPTable infoTable = new PdfPTable(8);
            infoTable.setWidthPercentage(100);
            infoTable.setWidths(new float[]{1f, 2f, 2f, 1f, 2.5f, 2f, 4f, 2f});
            String[] headers = {"序号", "身份", "姓名", "性别", "身高/体重", "比赛号码", "身份证号", "服装号码"};
            for (String h : headers) {
                PdfPCell cell = new PdfPCell(new Phrase(h, labelFont));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                cell.setPaddingTop(7f); // 4cm ≈ 113pt,每边加大间距
                cell.setPaddingBottom(7f);
                infoTable.addCell(cell);
            }
            int index = 1;
            for (Member m : team.getMembers()) {
                PdfPCell[] cells = new PdfPCell[] {
                        new PdfPCell(new Phrase(String.valueOf(index++), nameFont)),
                        new PdfPCell(new Phrase(m.getRole(), nameFont)),
                        new PdfPCell(new Phrase(m.getName(), nameFont)),
                        new PdfPCell(new Phrase(m.getGender(), nameFont)),
                        new PdfPCell(new Phrase(m.getHeightWeight(), nameFont)),
                        new PdfPCell(new Phrase(m.getCode(), nameFont)),
                        new PdfPCell(new Phrase(m.getIdCard(), nameFont)),
                        new PdfPCell(new Phrase(m.getClothingSize(), nameFont))
                };
                for (PdfPCell cell : cells) {
                    cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                    cell.setPaddingTop(7f); // 增加内边距
                    cell.setPaddingBottom(7f);
                    infoTable.addCell(cell);
                }
            }
            document.add(infoTable);

            // --- 画布页(第二页) ---
            document.newPage();
            // 代表队名称
            Paragraph title = new Paragraph("队名:" + team.getName(), titleFont);
            title.setAlignment(Element.ALIGN_LEFT);
            document.add(title);
            document.add(Chunk.NEWLINE);
            // 底部头像(其实也是一个表格,但是没有边框)
            PdfPTable table = new PdfPTable(3); // 每行最多3个头像(适合纵向)
            table.setWidthPercentage(100);
            table.getDefaultCell().setBorder(Rectangle.NO_BORDER);

            for (Member member : team.getMembers()) {
                PdfPCell cell = new PdfPCell();
                cell.setBorder(Rectangle.NO_BORDER);
                // 图片
                if (member.getImageUrl() != null) {
                    try {
                        Image img = Image.getInstance(new URL(member.getImageUrl()));
                        img.scaleToFit(100, 120);
                        cell.addElement(img);
                    } catch (Exception e) {
                        // 可添加默认图
                    }
                }
                // 图片下的文字信息
                Paragraph info = new Paragraph();
                info.setLeading(14);
                info.add(new Chunk(member.getRole() + ":", labelFont));
                info.add(new Chunk(member.getName() + "\n", nameFont));
                info.add(new Chunk(member.getIdCard() + "\n", nameFont));
                info.add(new Chunk("号码:" + member.getCode(), nameFont));
                cell.addElement(info);

                table.addCell(cell);
            }

            int remainder = team.getMembers().size() % 3;
            if (remainder != 0) {
                for (int i = 0; i < 3 - remainder; i++) {
                    PdfPCell empty = new PdfPCell();
                    empty.setBorder(Rectangle.NO_BORDER);
                    table.addCell(empty);
                }
            }

            document.add(table);
        }

        document.close();
    }

}

调用Controller

package com.an.pdfhandle.demos.web;

import com.an.pdfhandle.entity.Member;
import com.an.pdfhandle.entity.Team;
import com.an.pdfhandle.service.PdfExportService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

@RestController
public class PdfController {

    @GetMapping("/exportCanvasStyle")
    public void exportPdfCanvasStyle(HttpServletResponse response) throws Exception {
        // 模拟数据
        List<Team> teams = Arrays.asList(
                new Team("测试1代表队",
                        "大袋",
                        "15635748705",
                        "张三",
                        "李四",
                        "张三",
                        "15635748705"
                        , Arrays.asList(
                        new Member("张三", "运动员", "10","男","178/70KG","2XL", "110101199001010011", "http://101.37.161.72:8888/an/M00/00/00/rBoEmGRy2TuARDgsAADin4gdP7Q119.jpg"),
                        new Member("李四", "运动员", "11", "男","178/70KG","2XL", "110101199202020022", "http://101.37.161.72:8888/an/M00/00/00/rBoEmGRy2TuARDgsAADin4gdP7Q119.jpg")
                )),
                new Team("测试2代表队",
                        "大袋",
                        "15635748705",
                        "张三",
                        "李四",
                        "张三",
                        "15635748705", Arrays.asList(
                        new Member("王五", "运动员", "21", "男","178/70KG","2XL", "110101199303030033", "http://101.37.161.72:8888/an/M00/00/00/rBoEmGRy2TuARDgsAADin4gdP7Q119.jpg"),
                        new Member("赵六", "运动员", "22", "男","178/80KG","3XL", "110101199303030033", "http://101.37.161.72:8888/an/M00/00/00/rBoEmGRy2TuARDgsAADin4gdP7Q119.jpg")
                ))
        );

        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=teams.pdf");

        new PdfExportService().exportTeamCanvasStyle(teams, response.getOutputStream());
    }
}

到此结束,其实生成的比较粗糙,这里需要反思下,我的后端的排版布局能力还是比较差的,哈哈,有待提高。

使用前端直接用DOM元素往出导PDF的话会美观点,毕竟CSS用的更加顺手点, 使用VUE导出可以看我另一篇博客


网站公告

今日签到

点亮在社区的每一天
去签到