高效图片转PDF方案:基于itext7的Java高清生成实践
2026/9/7 12:55:20 网站建设 项目流程

简介:面向Java开发者的iText图片转高清PDF示例资源,解决将PNG等图片以较高清晰度写入PDF的常见需求。包内提供可直接运行的无坑版demo,覆盖图片加载、DPI设置、缩放适配与Document/PdfWriter操作等关键环节,也包含关键参数说明,便于快速理解iText生成PDF的核心流程。资源共11个文件,压缩包约1022KB,以iText核心jar、Java源码与工程配置为主,另有jsp、class、xml、txt等辅助文件,适合直接导入Java Web或普通工程中参考。已有5465人学习下载,适合需要处理图片转PDF功能的初中级Java工程师或相关项目开发者。借助该demo可重点掌握setDpi等参数对输出清晰度的影响,并对比三种缩放方式的适用场景,减少自行调试的坑点;readme文本也能帮助快速核对运行环境与依赖引入方式。入门者可对照示例快速搭建环境,进阶者也可将其作为封装图片转换工具的基础模板。

1. 为什么是 itext?先把图片转PDF的场景和方案捋清楚

图片转PDF这个需求,听着简单,但在实际业务里往往是“逼到墙角”才做的。我接触过不少类似情况:电商运营要把几十张商品实拍图、细节图合成一本产品画册发给客户;工程验收要把现场照片按顺序归档成凭证;后端服务要根据用户上传的多张图片自动生成一个可下载的PDF文件,这在电子合同、报告生成、打印预览类系统里几乎是标配。

如果你只是偶尔做一两张,用Word排版再另存为PDF确实够了。但一旦进入程序化、批量化阶段——比如接口收到一批图片,动态拼装PDF再返回给前端下载——就必须上代码方案。Java后端做这件事,itext基本是绕不开的选项。它是一套开源PDF处理类库,从生成、解析到编辑都能做,网上教程一抓一大把,社区成熟度非常高。

不过这里有个非常关键的前提:你现在搜到的大量教程,很多还停留在itext 5.x的写法,而itext 7.x的API已经做了较大调整。新项目建议直接上 itext7,老项目就安心跟着 5.x 走,别混着用。我下面所有示例均基于 itext7。如果代码编译不过,先看一眼依赖版本,大概率就是新旧API差异导致的。

还要说清楚:图片合成PDF真正麻烦的地方,从来不是“怎么把一张图塞进去”,而是如何保持高清、控制文件体积、处理批量场景下的内存和稳定性。标题里特意加“高清版”三个字,说明你大概率也遇到过图片被拉伸变形、画面模糊、PDF打开就卡、文件大得离谱这些问题。下面我逐个拆解。

2. itext7 生成PDF的最小可运行示例:先跑通再谈优化

2.1 Maven依赖与版本选择

使用 itext7 只需要引入核心模块。我习惯直接引itext7-core,它是个聚合POM,会把 kernel、io、layout、forms 等常用模块一起带进来,省去一个个加依赖的麻烦:

<dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>7.2.5</version> <type>pom</type> </dependency>

如果项目对依赖体积敏感,也可以分开引入:

<dependency> <groupId>com.itextpdf</groupId> <artifactId>kernel</artifactId> <version>7.2.5</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>io</artifactId> <version>7.2.5</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>layout</artifactId> <version>7.2.5</version> </dependency>

版本上建议用 7.2.x 以上的稳定版,新版本对JPEG、PNG的解析支持更完善,内存管理也更好。另外提一嘴:itext 的AGPL授权协议意味着如果做商业SaaS对外提供服务,需要了解其开源协议要求,个人学习和内部系统使用则问题不大。

2.2 最小代码:一张图片生成一页PDF

先看一个最简版本,方便理解整体结构:

import com.itextpdf.io.image.ImageData; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Image; import java.io.FileOutputStream; public class SimpleImageToPdf { public static void main(String[] args) throws Exception { String imagePath = "/path/to/your/image.jpg"; String pdfPath = "/path/to/output.pdf"; PdfWriter writer = new PdfWriter(new FileOutputStream(pdfPath)); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf); ImageData imageData = ImageDataFactory.create(imagePath); Image image = new Image(imageData); document.add(image); document.close(); } }

是不是觉得太简单了?确实,itext 把核心流程封装得很友好,本质上就三步:创建 PdfWriter、创建 PdfDocument、把 Image 加进 Document。但注意,这段代码生成的PDF有两个隐患:图片可能超出页面边缘被截断;图片按原始像素尺寸放置,和PDF页面的匹配关系完全没处理。

默认的A4页面是 595 x 842 点,一张 1200x1600 像素的手机照片直接放进去,很可能只显示了左上角一小块,或者被裁掉大半。所以要做出“高清且完整展示”的效果,就得主动控制图片与页面的关系。这个逻辑我放在下一节详细讲。

2.3 页面尺寸与图片尺寸的换算关系

这里需要先建立一个概念:PDF里的长度单位是“点”(point),1点 = 1/72英寸;图片尺寸单位是像素(pixel)。图片在PDF里显示多大,取决于你设置的缩放比例。一个 100x100 的图片不加缩放放进PDF,显示出来大约是 100/72 英寸,也就是3.5厘米左右,非常小。

所谓“高清PDF”,本质是在图片原始分辨率、页面大小、视觉清晰度之间找平衡。通用做法是按图片原始宽高比创建等比例的PDF页面,或者把图片等比缩放适配固定页面。方向不同,代码也不同,下一节给你两套方案。

3. 高清与适配的关键:页面跟随图片,图片自适应不拉伸

3.1 核心原则

我在实际项目里总结了两条必须遵守的原则:

  • 一条图片一页,页面比例尽量贴合图片比例;
  • 图片完整展示,等比缩放铺满可用区域,不裁剪、不拉伸。

第一点决定视觉体验,第二点决定是否“变形”。很多同学做出来图片模糊或者被压扁,基本都是在这两个环节上偷了懒。

3.2 方案A:页面尺寸跟随图片(适合图册、照片预览、打印)

这种方案下,PDF页面大小直接按图片像素尺寸设置,每页刚好放一张图,没有多余留白,适合做产品画册、相册类应用。

import com.itextpdf.io.image.ImageData; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Image; import java.io.FileOutputStream; public class ImageToPdfAutoPage { public static void main(String[] args) throws Exception { String[] imagePaths = { "/path/to/image1.jpg", "/path/to/image2.png", "/path/to/image3.jpg" }; String pdfPath = "/path/to/output.pdf"; PdfWriter writer = new PdfWriter(new FileOutputStream(pdfPath)); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf); for (String imagePath : imagePaths) { ImageData imageData = ImageDataFactory.create(imagePath); Image image = new Image(imageData); float width = imageData.getWidth(); float height = imageData.getHeight(); PageSize pageSize = new PageSize(width, height); pdf.addNewPage(pageSize); image.setFixedPosition(pdf.getNumberOfPages(), 0, 0); image.scaleToFit(width, height); document.add(image); } document.close(); } }

几个细节解释一下:

  • ImageDataFactory.create读取图片后,getWidth()getHeight()拿到的是像素尺寸;
  • new PageSize(width, height)创建了和图片像素等大的页面,单位直接按点算;
  • image.setFixedPosition(pdf.getNumberOfPages(), 0, 0)把图片定位到当前页面左下角,坐标原点就在左下角;
  • scaleToFit(width, height)保证图片完整落在页面范围内。

这个方案的坑在于:如果图片分辨率极大,比如扫描件每张宽高都在5000像素以上,生成的PDF页面也会非常大,很多阅读器在缩略图模式下会卡顿。所以它更适合图片尺寸相对均衡的场景。如果担心页面过大,可以在创建 PageSize 时做等比缩放,把最长边限制在1600点左右,清晰度和文件体积之间比较平衡。

3.3 方案B:图片适配固定页面(适合合同附件、归档文件、统一打印)

如果要求产出的PDF所有页面统一为A4,方便打印、归档或对接下游系统,就采用图片等比缩放适配A4可用区域的方案。

import com.itextpdf.io.image.ImageData; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.AreaBreak; import com.itextpdf.layout.element.Image; import java.io.FileOutputStream; public class ImageToPdfFixedPage { public static void main(String[] args) throws Exception { String[] imagePaths = { "/path/to/image1.jpg", "/path/to/image2.png" }; String pdfPath = "/path/to/output.pdf"; PdfWriter writer = new PdfWriter(new FileOutputStream(pdfPath)); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf, PageSize.A4); float pageWidth = PageSize.A4.getWidth() - document.getLeftMargin() - document.getRightMargin(); float pageHeight = PageSize.A4.getHeight() - document.getTopMargin() - document.getBottomMargin(); for (String imagePath : imagePaths) { ImageData imageData = ImageDataFactory.create(imagePath); Image image = new Image(imageData); image.scaleToFit(pageWidth, pageHeight); document.add(image); document.add(new AreaBreak()); } document.close(); } }

这里最关键的是scaleToFit(pageWidth, pageHeight):它会在保持宽高比的前提下,把图片等比缩放到不超过指定矩形区域,不会拉伸变形。

AreaBreak的作用是强制分页。如果不加这句话,Document 自动排版时可能会把多张图片挤到同一页,或因为图片太大自动换页,总之行为不够可控。加上之后,每张图片单独占一页,干净利落。

这里还要强调一个很多人容易误解的点:缩放只会改变图片在PDF里显示的大小,不会提升图片本身的清晰度。一张 800x600 的图,放到整页也只是把像素拉大,该糊还是糊。所以上游图片源本身的分辨率必须过关。我通常会在接口里做一道前置校验:图片最长边低于1200像素,就提示来源不够清晰,建议换原图。

4. 更精细的控制:旋转校正、页边距与PDF体积管理

4.1 图片方向错乱的坑

手机拍照的图片经常携带EXIF方向信息,直接读像素数据渲染时,可能显示成横的或者倒的。这个坑特别隐蔽:你在系统图片查看器里看到的是正的,用 itext 读出来却是歪的。itext7 对大部分标准JPEG图片会自动识别EXIF方向做校正,但某些非标准编码的图片仍然可能出问题。

稳妥做法是在生成PDF前统一处理图片方向。可以借助metadata-extractor库读取EXIF方向值,再根据方向决定旋转角度:

<dependency> <groupId>com.drewnoakes</groupId> <artifactId>metadata-extractor</artifactId> <version>2.19.0</version> </dependency>

用 Java 的 ImageIO 将图片旋转成标准正向,再输出为临时JPEG文件交给 itext 使用。代码不复杂,但实际项目里非常实用,尤其是做相册、证件照、扫描件归一化这类功能时。

旋转后再合成PDF,虽然多了一次读改写,但在稳定性和最终效果上非常值。记得处理完后清理临时文件。

4.2 自定义页边距与多图布局

Document默认页边距左右36点、上下54点,对图片类PDF来说通常够用。但很多场景希望图片顶到页面边缘、不要白边,尤其是做海报或对比图时:

Document document = new Document(pdf, PageSize.A4); document.setMargins(0, 0, 0, 0);

注意,设置零边距后如果打印机不支持无边距打印,边缘内容可能被裁掉。我的建议是:如果是屏幕查看为主的PDF,零边距效果很好;如果要打印,至少保留5~10点边距。

还有一类需求是把多张图放在同一页里做对比,比如两两对比、九宫格预览。这时不该依赖自动排版,而是用绝对定位:

Image img1 = new Image(ImageDataFactory.create("/path/img1.jpg")).scaleAbsolute(200, 200); Image img2 = new Image(ImageDataFactory.create("/path/img2.jpg")).scaleAbsolute(200, 200); img1.setFixedPosition(1, 36, 600); img2.setFixedPosition(1, 246, 600); document.add(img1); document.add(img2);

setFixedPosition(int pageNumber, float x, float y)的坐标原点在页面左下角。这种布局方式需要手动计算坐标,适合排版规则固定的场景。我做报告生成时经常用这一招把封面LOGO放在固定位置、正文图片排在另一块区域,自由度比自动排版高很多。

4.3 控制PDF体积:压缩质量与编码选择

图片原样嵌入PDF,PDF体积约等于所有图片体积之和,甚至更大。十几张高清照片动辄几十MB,传给别人或上传服务器都很痛苦。

控制体积最有效的手段是在进入 itext 前做好图片预处理:

  • 用 ImageIO 或 Thumbnailator 把大图等比缩放到目标宽度;
  • JPEG编码质量控制在 85% 左右,肉眼几乎无差异,体积却能下降不少;
  • 带透明通道的PNG通常体积大,如果不需要透明度,转成JPEG再合成;

在 itext 层面,可以设置压缩级别:

PdfWriter writer = new PdfWriter(new FileOutputStream(pdfPath)); writer.setCompressionLevel(9); // 0-9,9为最高压缩

setCompressionLevel(9)对PDF内部对象流做压缩,能进一步减小文件体积,但对已经嵌入的图片数据压缩空间有限,大头还是图片本身。

我做产品图册时的前置流程是:把图片统一 resize 到最长边 2200 像素,JPEG质量 88%,再交给 itext 合成PDF。实测一组20张手机原图(总大小约80MB),处理后生成的PDF大概8MB,清晰度在手机和电脑屏幕上完全够用。如果客户硬性要求超高精度,再走不压缩的原图路线,但这种情况占比不高。

5. 批量处理与内存管理:图片再多也不卡死

5.1 边遍历边写入,别把所有图片一次load进内存

新手最容易犯的错误是先把所有图片读成Image对象存进 List,最后再一次性加入PDF。少量图片没问题,一旦超过几十张高清图,JVM 很可能直接 OutOfMemoryError。

正确做法是边遍历边写:

public void createPdfFromImages(List<String> imagePaths, String pdfPath) throws Exception { try (PdfWriter writer = new PdfWriter(new FileOutputStream(pdfPath)); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf, PageSize.A4)) { float pageWidth = PageSize.A4.getWidth() - document.getLeftMargin() - document.getRightMargin(); float pageHeight = PageSize.A4.getHeight() - document.getTopMargin() - document.getBottomMargin(); for (String imagePath : imagePaths) { ImageData imageData = ImageDataFactory.create(imagePath); Image image = new Image(imageData); image.scaleToFit(pageWidth, pageHeight); document.add(image); document.add(new AreaBreak()); } } }

ImageDataFactory.create读取图片后,在写入PDF时数据会被消费,之后ImageData就可以被垃圾回收。使用try-with-resources确保流及时关闭,避免文件句柄泄漏。对大部分批量场景来说,这个模式已经足够稳定。

5.2 JVM参数与文件格式选择

如果处理的是超高清扫描件或超大尺寸TIFF,建议把启动参数调大:

java -Xms512m -Xmx2g -jar your-app.jar

在服务器上批量生成PDF时,堆内存给足很重要。但别一上来就几十G,绝大多数场景 1GB~2GB 堆内存已经足够。另外,itext7 解析PNG通常比JPEG慢,内存占用也更高。如果图片来源可控,优先把PNG转成JPEG再合成,生成速度和内存稳定性都会有明显改善。

5.3 并发生成与文件安全写入

涉及并发生成PDF时,建议用线程池固定并发数(比如4~8),每个任务创建独立的 PdfDocument,任务之间不共享任何状态。itext 本身在单线程内使用没问题,但同一个 PdfDocument 实例被多线程共享写入,会出现不可预期的异常或文件损坏。

还有一个非常重要的习惯:先写临时文件,生成完成后再原子替换目标文件。直接写目标文件,一旦中途异常,会留下一个残缺的半截PDF,用户下载后打不开,体验极差。统一改成临时文件加Files.move原子替换后,我再也没收到过“下载的PDF损坏”的反馈。

Files.move(tempFile, Path.of(targetPdfPath), StandardCopyOption.REPLACE_EXISTING);

6. 常见异常与排查流程

6.1 图片读取失败:Unsupported Image Type 与格式伪装

itext7 支持的图片格式包括JPEG、PNG、GIF、BMP、TIFF、SVG(SVG需要额外模块)。如果遇到Unsupported Image Type,先确认文件扩展名和真实格式是否一致。现在很常见的情况是:文件后缀是 .jpg,但实际内容是 WebP 或 PNG 编码。

WebP 是重灾区。浏览器和手机App里大量使用 WebP,后端直接读 WebP 给 itext 就会报错。解决思路有两个:前端上传时统一转成 JPEG/PNG,或者后端用 WebP 解码插件先转成 BufferedImage 再重新编码为 JPEG。从整个项目稳定性看,我更推荐前者,因为数据标准化在入口处解决,后面所有环节都省心。

6.2 中文乱码问题

如果你在图片PDF里还要加标题、说明文字,要特别注意 itext7 默认字体不支持中文,直接写中文会变成方块乱码。解决办法是注册中文字体:

PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");

更稳妥的做法是直接指定服务器上的中文字体文件:

PdfFont font = PdfFontFactory.createFont("/path/to/simsun.ttf", PdfEncodings.IDENTITY_H);

然后设置给文本对象:

document.add(new Paragraph("这是高清图片PDF").setFont(font).setFontSize(16));

这是在图片PDF基础上加说明文字时最常见的坑:图片正常,中文字体全变方块。

6.3 生成出来有空白页

空白页问题我在使用addNewPagesetFixedPosition时遇到过。原因通常是 Document 的自动分页和手动建页行为叠加,导致多出一页。排查思路很简单:先写最小复现代码,去掉 Document 的自动布局,完全用 addNewPage 和 setFixedPosition 控制页面,确认能复现后再逐项检查哪一步插入了空白。

实际项目中我推荐一种更稳的写法:不用 Document 自动分页,每处理一张图就pdf.addNewPage(PageSize.A4)一次,然后用image.setFixedPosition(pdf.getNumberOfPages(), margin, margin)固定位置。虽然代码啰嗦一点,但页面完全可控,不会有空白页的惊喜。

7. 前后端联动的完整项目方案

很多项目里图片转PDF不是孤立功能,而是整个业务链路中的一环。前端上传图片,后端处理生成PDF,再返回给前端下载。这个流程里存在几个容易忽略的设计点。

7.1 上传端的预处理

前端上传图片时,务必限制格式和大小。我见过不少系统不限制类型,结果用户传了GIF动图、WebP透明图、TIFF扫描件,后端解析各种兼容问题。最省心的方案是前端先通过 canvas 统一把图片转成 JPEG,设置最长边不超过2400像素、质量0.88,再传给后端。这样后端拿到的是标准化图片,合成PDF时异常率极低。

7.2 接口设计与异步任务

后端接口可以接收图片URL列表,也可以是二进制文件流。我实践中更推荐接收URL列表,因为图片通常已经存在对象存储上,后端只需下载并合成,内存压力更小。但要注意设置连接超时,避免某个图片地址不可访问时整个请求卡死。

如果图片数量多,同步生成PDF可能需要好几秒甚至十几秒,用户等在线接口容易超时。更合理的做法是异步化:接口先返回任务ID,后端线程池处理,完成后通过回调或轮询把PDF地址返回给前端。这个模式虽然复杂一点,但用户体验和系统稳定性都更好。

7.3 结果文件的存放

生成PDF后,建议直接上传到对象存储,返回一个PDF的访问URL,而不是把字节流直接返回给前端。原因有三个:

  • 浏览器直接下载大字节流容易中断;
  • 对象存储往往带CDN加速,用户下载体验更好;
  • PDF文件可以复用,避免相同内容反复生成。

如果只是内部工具,直接把字节流返回给前端也没问题,看场景取舍。

8. 一套可直接参考的完整模板代码

最后分享我在实际项目中用的一套模板代码。它把前面提到的点都整合起来了:A4固定页面、等比缩放、图片方向归一化、质量压缩、自动分页、临时文件原子替换、异常兜底。

import com.itextpdf.io.image.ImageData; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.AreaBreak; import com.itextpdf.layout.element.Image; import javax.imageio.ImageIO; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.List; public class HighQualityImagePdfGenerator { private static final float MAX_WIDTH = 1600f; private static final float MARGIN = 20f; public static void main(String[] args) throws Exception { List<String> imagePaths = List.of( "/path/to/img1.jpg", "/path/to/img2.png" ); generateHighQualityPdf(imagePaths, "/path/to/output.pdf"); } public static void generateHighQualityPdf(List<String> imagePaths, String targetPdfPath) throws Exception { Path tempFile = Files.createTempFile("preview_", ".pdf"); try (OutputStream out = new FileOutputStream(tempFile.toFile())) { PdfWriter writer = new PdfWriter(out); writer.setCompressionLevel(9); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf, PageSize.A4); document.setMargins(MARGIN, MARGIN, MARGIN, MARGIN); float pageWidth = PageSize.A4.getWidth() - 2 * MARGIN; float pageHeight = PageSize.A4.getHeight() - 2 * MARGIN; for (String imagePath : imagePaths) { File originalFile = new File(imagePath); if (!originalFile.exists()) { System.err.println("图片不存在,跳过: " + imagePath); continue; } BufferedImage bufferedImage = ImageIO.read(originalFile); if (bufferedImage == null) { System.err.println("图片解析失败: " + imagePath); continue; } BufferedImage normalizedImage = normalizeAndScale(bufferedImage, MAX_WIDTH); File tempImageFile = Files.createTempFile("normalized_", ".jpg").toFile(); ImageIO.write(normalizedImage, "jpg", tempImageFile); ImageData imageData = ImageDataFactory.create(tempImageFile.getAbsolutePath()); Image image = new Image(imageData); image.scaleToFit(pageWidth, pageHeight); pdf.addNewPage(PageSize.A4); image.setFixedPosition(pdf.getNumberOfPages(), MARGIN, MARGIN); document.add(image); Files.deleteIfExists(tempImageFile.toPath()); } document.close(); } Files.move(tempFile, Path.of(targetPdfPath), StandardCopyOption.REPLACE_EXISTING); } private static BufferedImage normalizeAndScale(BufferedImage source, float maxSide) { int width = source.getWidth(); int height = source.getHeight(); float max = Math.max(width, height); if (max <= maxSide) { return source; } float scale = maxSide / max; int newWidth = Math.max(1, Math.round(width * scale)); int newHeight = Math.max(1, Math.round(height * scale)); BufferedImage resized = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g = resized.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.drawImage(source, 0, 0, newWidth, newHeight, null); g.dispose(); return resized; } }

这套模板直接用于生产问题不大,几个关键点再强调下:

  • 先用 ImageIO 读取图片并重新编码为JPEG,顺带统一了格式;
  • 等比缩放到最长边不超过1600像素,清晰度和体积比较平衡;
  • 临时文件加原子替换,避免生成半成品PDF;
  • 每张图单独 addNewPage 和 setFixedPosition,保证一图一页且没有空白页。

如果需要处理SVG图片,itext 有专门的svg模块,配置方式类似。不过SVG转PDF对字体和路径兼容性要求较高,遇到显示异常时,建议先用工具转成PNG再做合成。

我在实际项目里还遇到过图片自带说明文字、要把标题放在图片下方的需求。也不复杂,用Paragraph配合setFixedPosition或者在图片下方画一个文本块就能解决,注意中文字体问题即可。

结尾

算下来,用 itext 做图片转PDF,最值得花时间的不是API本身,而是对业务场景的判断:到底让页面跟随图片,还是图片适配固定页面?图片分辨率有没有底线要求?生成速度重要还是文件体积重要?这几个问题想清楚了,代码自然就顺了。

我自己在这条路上踩过的坑,无非是格式不支持、图片旋转、内存溢出、空白页、文件写坏这几类。每解决一个,后面的项目就少踩一类。希望这篇文章能让你少走点弯路,直接拿着模板改改就能用。

最后再分享一条经验:任何图片转PDF功能上线前,记得用一批包含各种尺寸、格式、方向的原图做一轮压力测试。别只看单张效果,批量跑一次,观察内存和CPU曲线,把问题提前暴露出来,比上线后被用户用真实数据打脸强得多。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询