使用itextpdf动态生成PDF

embedded/2025/2/13 2:31:55/

前言

在有些场景下我们可能需要根据指定的模板来生成 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、编写代码

java">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 = "国铁集团";// 生成PDFgeneratePdf(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();}// 关闭PdfStamperstamper.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.ppmy.cn/embedded/161753.html

相关文章

json转excel,在excel内导入json, json-to-excel插件

简介 JSON 转 Excel 是一款 Microsoft Excel 插件&#xff0c;可将 JSON 数据转换为 Excel 表格。 要求 此插件适用于以下环境&#xff1a;Excel 2013 Service Pack 1 或更高版本、Excel 2016 for Mac、Excel 2016 或更高版本、Excel Online。 快速开始 本快速开始指南适用…

UIAbility 生命周期方法

生命周期流程图 UIAbility的生命周期官方文档地址https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/uiability-lifecycle-V13 1. onCreate(want: Want, launchParam: LaunchParam) 触发时机&#xff1a;Ability首次创建时 作用&#xff1a;初始化核心资源…

Ubuntu 多版本 gcc 配置常用命令备忘

用的频率不高&#xff0c;总忘记具体参数 1&#xff0c;安装多版本 gcc 以 gcc-11 和12 为例&#xff1a; sudo apt-get install gcc-11 gcc-12 sudo apt-get install gcc-11 gcc-12 2&#xff0c;配置多版本 gcc gcc 与 g 一起配置进数据库中&#xff1a; sudo update-a…

java基础6(黑马)

一、static 1.static修饰成员变量 static&#xff1a;叫静态&#xff0c;可以修饰成员变量、成员方法。 成员变量按照有无static&#xff0c;分两种。 类变量&#xff1a;有static修饰&#xff0c;属于类&#xff0c;在计算机中只有一份&#xff0c;会被类的全部对象共享。…

15、深度学习-自学之路-反向传播程序展示、激活函数的应用,反向权重的更新、2层神经网络的应用,输入输出相关性的理解。

这个里面要学习和展示的内容会比较多&#xff0c;需要好好的认真思考 第一个要思考的就是&#xff1a;输入和输出相关性的理解&#xff0c;我们先拿一层的神经网络来说明一下&#xff0c; 输入有2个因素&#xff0c;对应有两个权重&#xff0c;输出有一个结果。 输入的两个因…

Mockito从入门到精通教程大纲(基于JUnit 5)

Mockito从入门到精通教程大纲&#xff08;基于JUnit 5&#xff09; 第一章&#xff1a;环境准备与基础概念 单元测试核心原则 测试金字塔&#xff08;单元/集成/端到端测试&#xff09;为什么需要Mock&#xff1f;依赖隔离的重要性 环境搭建 JUnit 5 Mockito 5 依赖配置&…

【测试开发】Python+Django实现接口测试工具

PythonDjango接口自动化 引言&#xff1a; 最近被几个公司实习生整自闭了&#xff0c;没有基础&#xff0c;想学自动化又不知道怎么去学&#xff0c;没有方向没有头绪&#xff0c;说白了其实就是学习过程中没有成就感&#xff0c;所以学不下去。出于各种花里胡哨的原因&#xf…

服务器绑定 127.0.0.1 和 0.0.0.0 的区别

前言 IP 地址实际上并不是分配给计算机的&#xff0c;而是分配给网卡的&#xff0c;因此当计算机上存在多块网卡时&#xff0c;每一块网卡都会有自己的 IP 地址。 绑定 127.0.0.1 是绑定到 lookback 这个虚拟的本地回环接口&#xff0c;该接口只处理本机上的数据&#xff0c;…