Spring Boot 使用Itext绘制并导出PDF

最终效果

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

项目依赖

xml 复制代码
<!--    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>
        

实现代码

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

核心实现方法

java 复制代码
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

java 复制代码
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导出可以看我另一篇博客

相关推荐
humors2217 小时前
pdf工具分享
pdf·工具·程序·网站·转换·处理
JH30739 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
qq_124987075312 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_12 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
2301_8187320612 小时前
前端调用控制层接口,进不去,报错415,类型不匹配
java·spring boot·spring·tomcat·intellij-idea
新缸中之脑13 小时前
用MonkeyOCR解析复杂PDF
pdf
汤姆yu16 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶16 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
biyezuopinvip17 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
JavaGuide17 小时前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot