当前位置: 首页 > article >正文

使用itextpdf动态生成PDF

前言

在有些场景下我们可能需要根据指定的模板来生成 PDF,比如说合同、收据、发票等等。因为 PDF 是不可编辑的,所以用代码直接对 PDF 文件进行修改是很不方便的,这里我是通过 itextAdobe Acrobat 来实现的,以下就是具体实现方法。

一、准备模板

Adobe Acrobat 是由 Adobe 公司开发的一款 PDF(Portable Document Format,便携式文档格式)编辑软件。借助它,你可以以 PDF 格式制作和保存文档 ,以便于浏览和打印,或使用更高级的功能。

说白一点就是 Adobe Acrobat 可以让你的 PDF 文件编程可编辑文件,PDF 文件可编辑的话,使用代码去修改就会方便很多。

adobe 中文官网:https://www.adobe.com/cn/

Adobe Acrobat 中文官网:https://www.adobe.com/cn/acrobat.html

如果你之前没有使用过这个软件,可以在上面我提供的官网里面去下载
在这里插入图片描述

下载完,打开该软件大概是这个样子的
在这里插入图片描述

2、创建模板
  • 使用Adobe编辑打开
    在这里插入图片描述
  • 点击准备表单,会自动帮你创建好表单,要是没有这个按钮,就去 更多工具 里添加
    在这里插入图片描述
  • 此按钮可以用户自定义添加表单
    在这里插入图片描述

3、引入依赖

		<dependencies>
			<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itextpdf</artifactId>
                <version>5.5.13</version>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
            <!--字体集-->
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext-asian</artifactId>
                <version>5.2.0</version>
            </dependency>
            
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>pdfbox</artifactId>
                <version>2.0.27</version>
            </dependency>
            
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>pdfbox-tools</artifactId>
                <version>2.0.27</version>
            </dependency>
        </dependencies>

4、编写代码

package com.hnys.zhct.common.core.utils;

import com.hnys.zhct.common.core.domain.ProblemSolutionVO;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 生成pdf的工具类
 *
 * @author Simon
 */
@Slf4j
public class GeneratedPdfUtils {

    public static void main(String[] args) {
        try {
            // 示例数据
            Map<String, Object> data = new HashMap<>();
            data.put("name", "lily");
            data.put("address", "麓谷");
            data.put("city", "changsha");
            data.put("state", "CN");
            data.put("zip", "123");
            // 例如:黑体字体
            String fontPath = "D:\\testImg\\myf\\simhei.ttf";
            // PDF文件路径
            String templatePath = "D:\\testImg\\template\\template.pdf", pdfPath = "D:\\output.pdf";
            // 水印内容
            String watermarkText = "国铁集团";
            // 生成PDF
            generatePdf(templatePath, pdfPath, data, fontPath, watermarkText);
            log.info("PDF生成成功!");

            // 输出目录
            String outputDir = "D:\\output_images";
            // 分辨率(DPI)通常300 DPI适用于高质量的图片,值越高质量越好,文件大小越大
            int dpi = 300;

            // 转换为PNG图片
            String imagePath = convertPdfToSingleLongPng(pdfPath, outputDir, dpi);

            // 打印生成的图片路径
            System.out.println("生成的图片: " + imagePath);


        } catch (IOException | DocumentException e) {
            log.error("PDF生成失败!");
            e.printStackTrace();
        }
    }

    /**
     * 通过PDF模板生成PDF文件
     *
     * @param templatePath PDF模板路径
     * @param outputPath   生成的PDF文件路径
     * @param data         要填充的数据,键为表单字段名,值为要填充的内容
     * @param fontPath 字体
     * @param watermarkText 水印文字
     * @throws IOException 文件流异常
     * @throws DocumentException 文件读取异常
     */
    @SuppressWarnings("unchecked")
    public static void generatePdf(String templatePath, String outputPath, Map<String,
            Object> data, String fontPath, String watermarkText)
            throws IOException, DocumentException {
        // 读取PDF模板
        PdfReader reader = new PdfReader(templatePath);
        // 创建PdfStamper对象
        PdfStamper stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputPath)));
        // 获取表单字段
        AcroFields form = stamper.getAcroFields();

        // 加载字体
        BaseFont baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

        // 设置表单字段的字体
        for (String fieldName : form.getFields().keySet()) {
            form.setFieldProperty(fieldName, "textfont", baseFont, null);
        }

        // 填充固定表单字段
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            String fieldName = entry.getKey();
            Object value = entry.getValue();
            if (!(value instanceof List)) {
                form.setField(fieldName, value.toString());
            }
        }

        // 处理动态的List数据
        List<ProblemSolutionVO> problemSolutions = (List<ProblemSolutionVO>) data.get("problemList");
        if (CollectionUtils.isNotEmpty(problemSolutions)) {
            PdfContentByte content = stamper.getOverContent(1);
            float startY = 700; // 起始纵坐标
            float margin = 20;  // 间距

            for (ProblemSolutionVO ps : problemSolutions) {
                // 添加问题描述段落;如果存在其他string类型的字段,copy以下这段代码即可,需要多次执行main方法来调整坐标位置
                content.beginText();
                content.setFontAndSize(baseFont, 12);
                content.setTextMatrix(50, startY);
                content.showText(ps.getProblemDescription());
                content.endText();
                startY -= 20;

                // 添加图片
                Image image = Image.getInstance(ps.getImagePath());
                float width = 300;
                float height = width * image.getHeight() / image.getWidth();
                image.scaleToFit(width, height);
                image.setAbsolutePosition(50, startY - height);
                content.addImage(image);
                startY -= height + margin;
            }
        }

        // 设置表单为不可编辑
        stamper.setFormFlattening(true);

        // 添加水印
        int totalPages = reader.getNumberOfPages();
        for (int i = 1; i <= totalPages; i++) {
            PdfContentByte watermarkContent = stamper.getOverContent(i);
            watermarkContent.saveState();
            watermarkContent.setFontAndSize(baseFont, 50);
            watermarkContent.setColorFill(BaseColor.LIGHT_GRAY);
            watermarkContent.setTextMatrix(300, 300);
            watermarkContent.showTextAligned(Element.ALIGN_CENTER, watermarkText, 300, 300, 45);
            watermarkContent.restoreState();
        }

        // 关闭PdfStamper
        stamper.close();
        reader.close();
    }

    /**
     * 将PDF文件转换为单个长PNG图片
     *
     * @param pdfPath  PDF文件路径
     * @param outputDir 输出图片的目录
     * @param dpi      图片分辨率(每英寸点数)
     * @return 生成的长图文件路径
     * @throws IOException 文件流异常
     */
    public static String convertPdfToSingleLongPng(String pdfPath, String outputDir, int dpi) throws IOException {
        // 加载PDF文件
        PDDocument document = PDDocument.load(new File(pdfPath));
        PDFRenderer renderer = new PDFRenderer(document);

        // 创建输出目录(如果不存在)
        File outputDirFile = new File(outputDir);
        if (!outputDirFile.exists()) {
            outputDirFile.mkdirs();
        }

        // 获取PDF页数
        int pageCount = document.getNumberOfPages();

        // 逐页渲染为图片并计算总高度
        int totalHeight = 0;
        int width = 0;
        // 多页pdf即多张图片
        BufferedImage[] images = new BufferedImage[pageCount];
        for (int i = 0; i < pageCount; i++) {
            BufferedImage image = renderer.renderImageWithDPI(i, dpi);
            images[i] = image;
            totalHeight += image.getHeight();
            width = Math.max(width, image.getWidth());
        }

        // 创建一个新的长图
        BufferedImage longImage = new BufferedImage(width, totalHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = longImage.createGraphics();
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, totalHeight);

        // 将所有图片合并到长图中
        int y = 0;
        for (BufferedImage image : images) {
            g2d.drawImage(image, 0, y, null);
            y += image.getHeight();
        }
        g2d.dispose();

        // 保存长图
        String longImagePath = outputDir + "/long_image.png";
        ImageIO.write(longImage, "png", new File(longImagePath));

        // 关闭PDF文档
        document.close();

        return longImagePath;
    }

}


http://www.kler.cn/a/542258.html

相关文章:

  • 前端快速生成接口方法
  • Android和DLT日志系统
  • C# 上位机--变量
  • PDF Shaper:免费多功能 PDF 工具箱,一站式满足您的 PDF 需求!
  • 算法04-希尔排序
  • 大语言模型RAG,transformer
  • OpenCV 相机标定流程指南
  • 前端技术学习——ES6核心基础
  • 01.Docker 概述
  • vue3-01-初识vue3相对于vue2的提升与比较,使用vue-cli创建项目,使用vite构建工具创建项目
  • C++ -- vector的模拟实现
  • hive spark读取hive hbase外表报错分析和解决
  • vue2 导出Excel文件
  • 自动化办公|xlwings快速入门
  • 物联网综合性应用之网关设计
  • Linux防火墙设置
  • 【PS 2022】Adobe Genuine Service Alert 弹出
  • 详细代码篇:python+mysql +h5实现基于的电影数据统计分析系统实战案例(二)
  • 100.14 AI量化面试题:模型蒸馏(Model Distillation)和模型微调(Fine-tuning)的异同点
  • 1.1 CXX-Qt入门指南
  • 网络工程师 (32)TRUNK
  • 收集一些嵌入式相关学习资料的网址(持续更新)
  • 【JVM详解四】执行引擎
  • webpack配置之---output.chunkLoading
  • 客户端布局 --- 左侧导航栏右侧内容页
  • iOS主要知识点梳理回顾-5-运行时方法交换