尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用iText把图片压成高清PDF:DPI与缩放实战指南
简介面向需要将图片高质量转为PDF的Java开发者这是一份基于iText的无坑实操示例包。资源以完整的demo工程呈现重点演示如何通过setDpi设置300 DPI、使用scaleToFit控制图片缩放从而避免生成模糊PDF覆盖PNG、JPEG等常见格式可直接迁移到批量报表、电子书合成等办公自动化场景。压缩包共11个文件主要包含iText.jar依赖、Java示例源码、JSP测试页面、编译后的class以及工程配置文件与readme说明整体大小约1022KB结构简短清晰。已有5467人学习下载适合正在学习iText或想快速获得可运行参考实现的开发者对照源码即可理解高清转换的关键参数与配置步骤。1. 用 iText 把图片压成高清 PDF这次把 DPI 和缩放的坑一次填平做导出功能时总会被问「能不能把图片导成 PDF最好清楚一点」。直接用 iText 的 Image 往文档里一塞出来要么图被拉伸得没法看要么在屏幕上正常、打印或放大后糊成一团。这个「清晰」的坑其实不只在压缩质量而在 DPI 与页面尺寸的换算关系。本文要拆的这套 iText 图片生成 PDF高清版方案核心就三件事把 PNG/JPG 按原始像素塞进自定义尺寸的 PDF 页面、用缩放与 DPI 参数保证视觉清晰度、以及处理 iText 5.x 里图片对象的各种暗坑。适合正在做订单回执、证书导出、图片存档这类功能的开发者也适合想弄清 iText 里图片为啥「不清晰」的熟手。2. 高清 PDF 的底层逻辑像素、DPI 与页面尺寸的三角关系2.1 为什么直接塞图片会糊iText 里的 Image 对象本身不带「清晰度」属性它记录的只是像素宽高和缩放比例。PDF 渲染时图片会按页面坐标系统中的单位去绘制。默认情况下iText 会把图片按原始像素的 72 DPI 换算成 PDF 里的物理尺寸。也就是说一张 3000x2000 像素的图插入后会被当成 3000/72 x 2000/72 英寸即约 105.8 x 70.5 厘米。这带来两个典型问题一是当你把图片缩得很小放在 A4 页面里PDF 阅读器缩放时图片会被重新采样看起来没问题但一旦用户放大或拿去印刷就暴露像素不足二是当你用 setScalePercent(50) 强行缩小图片的实际像素并没有变只是显示尺寸缩了存储体积和渲染开销一点没省。真正的「高清」应该是让图片在 PDF 里的物理尺寸与它的像素密度匹配也就是让 DPI 落在合理范围而不是靠缩放百分比去糊弄。2.2 这套方案的选型iText 5.x 全家桶我用的依赖是 iText 5.5.13.3 的 com.itextpdf 包不是 iText 7。原因很简单iText 7 的 API 变化太大网上的老教程大多基于 5.x而且 5.x 的 Image、PdfWriter、Document 这套组合处理「图片插入 PDF」这个单一场景代码量最少。如果你已经在用 iText 7这套思路也能平移只是类的命名空间从 com.itextpdf.text 换成了 com.itextpdf.layout细节略有差异。在 pom.xml 里引入dependency groupIdcom.itextpdf/groupId artifactIditextpdf/artifactId version5.5.13.3/version /dependency逻辑说明iText 5.x 的核心入口是 Document 和 PdfWriter它们配合实现了「添加内容 → 写入文件」的模型。相比 7.x 的布局系统5.x 更贴近底层适合我们手动控制页面尺寸。参数说明itextpdf 5.5.13.3 是 5.x 最后的稳定版本支持 JDK 8 及以上。如果项目还在用 JDK 7需要降到 5.5.12 或更早版本。注意别误引入 itext-asian这个场景用不上。2.3 预先算好页面尺寸按像素定 PDF 大小高清的关键一步不是插入图片后去缩放而是在创建 Document 之前就把 PDF 页面尺寸设置为图片的物理尺寸。这样图片插入后是 1:1 放进页面的无需缩放渲染时也不会有重新采样带来的模糊。实现方式是用图片的像素宽高乘以一个目标 DPI 换算系数。BufferedImage img ImageIO.read(new File(input.png)); int pixelWidth img.getWidth(); int pixelHeight img.getHeight(); // 目标 DPI比如 150打印和屏幕显示都比较清晰 float dpi 150f; float scaleFactor 72f / dpi; float pageWidth pixelWidth * scaleFactor; float pageHeight pixelHeight * scaleFactor; Document doc new Document(new Rectangle(pageWidth, pageHeight), 0, 0, 0, 0); PdfWriter.getInstance(doc, new FileOutputStream(output.pdf)); doc.open();逻辑说明pixelWidth * 72 / dpi把像素点转换成 PDF 的点单位。PDF 内部以点point为单位1 英寸 72 点。当 DPI 设为 150 时一张 1500 像素宽的图片在 PDF 里就是 1500/150*72 720 点宽即 10 英寸。图片插入后不缩放物理尺寸和像素密度正好匹配。参数说明new Rectangle(pageWidth, pageHeight)是自定义页面大小。四个 0 是页边距图片场景务必设为 0否则 iText 默认会有 36 点左右的边距导致图片被挤小。DPI 建议取 96屏幕到 300印刷之间超过 300 对大多数场景无意义反而让文件变大。3. 实操把 PNG / JPG 批量写入 PDF 并控制清晰度3.1 拿到图片后先做预处理高清除了 DPI还跟源图本身有关。如果拿到的图本身只有 500px 宽那无论 DPI 怎么设都不可能清晰。我一般会在插入前检查图片的像素密度如果低于预期可以选择放大图片插值或提示调用方更换原图。放大图片用 AffineTransform 插值是个常规方案但不建议放太大超过 1.5 倍就属于聊胜于无。BufferedImage src ImageIO.read(new File(raw.png)); int targetWidth 1200; double scale targetWidth / (double) src.getWidth(); int targetHeight (int) (src.getHeight() * scale); BufferedImage scaled new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g scaled.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.drawImage(src, 0, 0, targetWidth, targetHeight, null); g.dispose(); ImageIO.write(scaled, png, new File(scaled.png));逻辑说明这段是做质量控制的前置步骤。先把源图读进来按目标宽度等比放大并重新绘制到新画布。缩放后的图片保存到临时文件后续插入 PDF 时直接用这个文件。参数说明VALUE_INTERPOLATION_BILINEAR 是双线性插值适合放大图片如果放大倍数较大可以换 VALUE_INTERPOLATION_BICUBIC效果更好但耗时增加。TYPE_INT_RGB 会丢弃透明通道如果原图是 PNG 带透明背景这个处理会造成黑底要注意保留 TYPE_INT_ARGB。3.2 图片插入与页面自适应两种策略处理单张图片时建议让 PDF 页面跟随图片尺寸。如果是一批不同尺寸的图片则有两种策略一是为每张图都单独建一页页面大小跟随图片二是统一页面大小图片缩放填充。前者更清晰后者适合归档打印。这里给出每张图独立成页的写法。import com.itextpdf.text.Document; import com.itextpdf.text.Image; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfWriter; File[] files new File(images).listFiles((dir, name) - name.toLowerCase().endsWith(.png) || name.toLowerCase().endsWith(.jpg)); Document doc new Document(); PdfWriter writer PdfWriter.getInstance(doc, new FileOutputStream(all.pdf)); doc.open(); for (File f : files) { Image img Image.getInstance(f.getAbsolutePath()); float imgWidth img.getWidth(); float imgHeight img.getHeight(); // 按目标 DPI 计算页面尺寸页面跟随图片 float dpi 150f; float scale 72f / dpi; Rectangle pageSize new Rectangle(imgWidth * scale, imgHeight * scale); doc.setPageSize(pageSize); doc.newPage(); // 图片位置原点在左下角无需额外偏移 img.setAbsolutePosition(0, 0); doc.add(img); } doc.close();逻辑说明Image.getInstance是 iText 5.x 加载图片的核心方法支持路径、URL 和字节数组。getWidth()返回的是图片的像素宽度不等同于 PDF 里的物理宽度。通过imgWidth * scale计算页面大小配合doc.setPageSize实现「一图一页、页随图走」。newPage()在循环里每次开启新页避免图片堆叠在同一页上。参数说明img.setAbsolutePosition(0, 0)把图片起点放在页面左下角。由于页边距为 0页面尺寸就是图片尺寸图片就能无偏移铺满整页。如果发现图片上下或左右留白检查是不是 Document 构造时没有传入零边距的 Rectangle。3.3 用压缩与 DPI 组合实现「高清且不臃肿」高清不等于文件大。PDF 内嵌图片时iText 默认不重新编码图片只是原样嵌入。原样嵌入 PNG 有时反而比 JPG 更占体积。通过 PdfWriter 的压缩级别和图片质量参数可以做到清晰度不变、体积下降。PdfWriter writer PdfWriter.getInstance(doc, new FileOutputStream(hd.pdf)); writer.setFullCompression(true); writer.setCompressionLevel(9); Image img Image.getInstance(photo.jpg); img.setDpi(150, 150); doc.add(img);逻辑说明setFullCompression(true)是对整个 PDF 内容流启用 Flate 压缩而不仅是图片流。setCompressionLevel(9)是 zlib 的压缩等级0 不压缩9 最高压缩。注意这两个设置影响的是 PDF 结构数据的体积对图片本身的编码没有影响。参数说明img.setDpi(150, 150)设置图片的 X/Y DPI。这个 DPI 主要影响 PDF 阅读器「显示尺寸」的计算不影响像素数据。如果前面已经用像素换算页面尺寸DPI 的作用被弱化但如果页面尺寸固定比如 A4这个 DPI 就变得重要因为阅读器会按 DPI 把图片渲染到对应尺寸。4. 避坑与常见问题高清导出路上的五个经典翻车现场4.1 图片怎么插都是糊的放大后马赛克严重现象无论 DPI 怎么设置生成的 PDF 放大后图片都模糊。原因源图本身的像素不够或者插入时把大图缩小显示。PDF 里的图片是栅格图放大超过 100% 必然模糊程序无法「无中生有」。解决先检查源图分辨率建议至少 1000px 宽再导出。如果源图不行用 3.1 节的双线性插值先放大到目标尺寸虽然不能完全还原细节但至少在普通缩放级别下观感好很多。另外确认代码里没有调用setScalePercent(50)之类的方法这类写法只是缩小显示尺寸不会增加清晰度。4.2 PNG 图片插入后背景变成黑色现象透明背景的 PNG 导出的 PDF 中透明区域变成了纯黑色块。原因iText 5.x 在处理某些 PNG 透明通道时会走 Jpeg 或 CCITT 编码路径导致 alpha 通道丢失或解析异常。尤其是带半透明渐变或 PNG-24 的图概率较高。解决在插入之前把 PNG 统一转换成不带透明通道的 JPG或者把透明区域替换成白色。转换用BufferedImage绘制到白色背景上即可。如果非要保留透明可以试试 iText 的Image.getInstance配合PngImage类但效果不稳定建议直接绕开。4.3 页面尺寸设了但图片还是被裁剪现象图片插入后四周被切掉了一部分尤其是大尺寸图片。原因当Document的页面尺寸小于图片的实际显示尺寸时iText 会保留图片原尺寸绘制超出页面的部分被裁剪。我见过同事把页面设成 A4然后直接插入一张 3000px 的图结果只显示左上角一块。解决按 2.3 节的方式用图片尺寸倒数推出页面大小再创建 Document。如果页面大小必须固定就要用img.scaleToFit(pageWidth, pageHeight)让图片缩放适应页面但那样清晰度会受影响。4.4 多张图生成 PDF 时只有第一张现象循环里 add 了多张图片但 PDF 里只有一张。原因Document默认是从第一页开始的循环中没有调newPage()导致后续图片都在第一页上而 iText 遇到页面放不下新内容时会自动翻页但如果图片用了绝对定位它不会主动触发翻页所有图片叠在了同一页上。解决每次 add 前调用doc.newPage()。注意如果是第一张图newPage()会创建一个空白页再插入图片导致第一页空白。可以用计数器判断第一次不调newPage()后续调用。4.5 生成的 PDF 体积巨大一张图占好几 MB现象简单的几张图生成的 PDF 竟然几十 MB。原因PNG 原样嵌入时不经过压缩24 位深、高分辨率的 PNG 本身就很占空间。如果页面尺寸设得过大图片在 PDF 里的显示尺寸也大但实际上没有重新压缩编码。解决图片统一转成 JPG 再插入JPG 质量控制在 0.85 左右见 5.2 节。配合 3.3 节的setFullCompression(true)和setCompressionLevel(9)通常能压掉 60% 的体积。5. 让清晰度再进一步统一图片格式与质量参数的调优习惯5.1 为什么我坚持先把图片转成 JPG 再插 PDF用 iText 处理图片导出最先要考虑的就是「源图格式太难伺候」。PNG 的透明通道、色彩空间、位深度各不相同iText 5.x 对 PNG 的解析依赖 Java 原生的 ImageIO遇到 CMYK 色彩空间或 16 位深度的 PNG 甚至会抛异常或生成颜色完全错乱的 PDF。相比之下JPG 只有一个 8 位 RGB 色彩空间iText 解析几乎零失败。所以我的习惯是所有图片先统一过一遍预处理管线统一转成 RGB JPG质量 0.85然后在插 PDF。这样做牺牲了一点透明通道但换来的是绝不出错、色彩一致、体积可控。如果你非要保留透明就单独为 PNG 走一套流程同时做好 4.2 节的避坑准备。public static File toJpeg(File pngFile) throws IOException { BufferedImage original ImageIO.read(pngFile); BufferedImage rgb new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g rgb.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, rgb.getWidth(), rgb.getHeight()); g.drawImage(original, 0, 0, null); g.dispose(); File jpg new File(pngFile.getParent(), pngFile.getName() .jpg); ImageIO.write(rgb, jpg, jpg); return jpg; }逻辑说明先把原图绘制到一个全新白底的 TYPE_INT_RGB 画布上淡化了透明像素再以 jpg 格式写出。g.setColor(Color.WHITE)设置背景色fillRect把画布铺满这一步把原来的透明区域变成了白色避免后续插 PDF 时出现黑底。参数说明TYPE_INT_RGB是 24 位 RGB不带 alpha 通道。ImageIO.write 的第二个参数 jpg 控制编码器。JPG 质量默认约 0.75如果要自定义得用 JPEGImageWriteParam 单独设置压缩质量后面参数解读会讲。5.2 控制 JPG 质量参数清晰与体积的平衡点JPG 是有损压缩质量参数决定了压缩率与失真程度。iText 本身不提供「JPG 质量」这样的接口质量的控制发生在上游的编码环节。用 ImageIO 的默认 write 方法拿不到质量参数需要显式设置。IteratorImageWriter writers ImageIO.getImageWritersByFormatName(jpg); ImageWriter writer writers.next(); ImageWriteParam param writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(0.85f); FileOutputStream fos new FileOutputStream(output.jpg); ImageOutputStream ios ImageIO.createImageOutputStream(fos); writer.setOutput(ios); writer.write(null, new IIOImage(rgb, null, null), param); ios.close(); fos.close(); writer.dispose();逻辑说明这里手动控制 JPEG 编码参数。setCompressionMode(MODE_EXPLICIT)打开手动设置压缩模式setCompressionQuality(0.85f)设置压缩质量。0.85 是我反复试下来清晰度与体积最平衡的值肉眼几乎看不出差异体积却比 0.95 小一半多。参数说明质量值范围 0f1f0.75 以下会出现明显压缩痕迹0.9 以上体积明显增大但肉眼收益有限。如果做印刷级输出可以设到 0.920.95但要注意文件体积成倍增长。5.3 验证输出肉眼之外用像素数据说话生成 PDF 后验证「高清」不能光靠眼睛。我一般会用一个小工具脚本把 PDF 页面转成图片然后对比源图的像素数据和页面渲染出的图片像素数据看有没有明显差异。这里提供一个简单的思路用 PDF 渲染库把 PDF 转图片再比较分辨率。# 需要安装 pdf2image 之类的渲染组件 python -m pip install pdf2image python -c from pdf2image import convert_from_path images convert_from_path(output.pdf, dpi150) print(images[0].size) print(images[0].save(page1.png)) 逻辑说明convert_from_path指定 DPI 为 150 渲染 PDF得到的是这个 DPI 下的位图。如果渲染出的图片尺寸接近源图尺寸按比例换算说明 PDF 里的图片物理尺寸和像素密度是匹配的没有发生明显的重新采样或缩放失真。如果渲染出的图明显小于源图按 DPI 换算出的尺寸那说明图片在 PDF 里被缩小了阅读器在放大查看时必然模糊。参数说明dpi150必须和你在 PDF 里预设的 DPI 一致否则渲染尺寸对不上。这个验证脚本适合本地调试不建议写进生产逻辑毕竟渲染 PDF 属于重计算没法用在每个导出请求里。从那以后我每次做 iText 的图片 PDF 导出都会先算一遍页面尺寸和 DPI 的换算关系再决定压缩级别而不是直接把图片往 Document 里一丢。这套流程帮我处理过证书批量导出、单据归档、甚至是某个图像处理 Demo 的结果预览几乎没有再被「图片不清晰」这种问题折腾过。希望这些参数细节和踩坑记录能帮你绕开我当年走过的弯路。本文还有配套的精品资源点击获取
RELATED

相关推荐

WebRTC通话全链路解析:从SDP协商到ICE穿透与拥塞控制

WebRTC通话全链路解析:从SDP协商到ICE穿透与拥塞控制

1. 为什么说WebRTC不是“另一个视频插件”,而是一场通信基建的静默革命很多人第一次听说WebRTC,是在某个在线会议页面弹出“请允许摄像头访问”的提示框时。它藏在浏览器地址栏后面,不显山不露水,却让全球数十亿人每天在不装任何客…

📅 2026/10/18 3:07:07
macOS权限管理实战:OC授权检测、申请与系统设置跳转全解析

macOS权限管理实战:OC授权检测、申请与系统设置跳转全解析

1. 从一次授权弹窗被拒说起:macOS 权限体系的底层逻辑做 macOS 端 Objective-C 开发的同学,大概率都遇到过这样的场景:功能写完了,本地跑得好好的,打包给测试同事一装,摄像头打不开、麦克风没声音、屏幕录制…

📅 2026/10/18 3:07:07
GhostTrack从0跑通IP查询、手机号归属地与用户名查询

GhostTrack从0跑通IP查询、手机号归属地与用户名查询

GhostTrack从0跑通IP查询、手机号归属地与用户名查询 【免费下载链接】GhostTrack Useful tool to track location or mobile number 项目地址: https://gitcode.com/GitHub_Trending/gh/GhostTrack 陌生号码在屏幕上跳出,你犹豫接不接;日志里冒出…

📅 2026/10/18 3:07:07
MORE NEWS

更多资讯

📰

不用DAC也能播音频:STM32用PWM输出WAV的完整工程方案

简介:这是一个面向STM32开发者的完整音频播放工程,演示借助PWM接口播放SD卡中的WAV文件,不依赖外部DAC,通过定时器与GPIO即可输出音频。工程覆盖FatFS文件系统挂载、WAV头解析、PCM数据到PWM占空比转换、定时器中断实时更新等关键…

📰

JavaEE二手图书交易平台实战拆解:从JSP+Servlet+JDBC到部署避坑

简介:基于JavaEE的二手图书交易平台设计源码与配套高分报告,是一套面向Java Web课程设计、期末大作业的完整可运行项目。平台覆盖二手图书发布、浏览检索、下单购买、订单管理、后台数据维护等典型业务,界面采用Bootstrap响应式布局&#xff…

📰

硬盘分区合并全攻略:C盘扩容与无损合并方法详解

1. 先说清楚:合并分区到底是怎么回事作为一个折腾电脑十几年的人,我隔三差五就会碰到有人来问:“我C盘又红了,能不能把D盘的空间匀给它?”或者“当时装系统分区没规划好,现在盘符又多又乱,能不能…

📰

Django+Vue构建茶叶电商系统:从商品建模到订单全栈实战

接到一个茶叶销售系统的开发需求时,我的第一反应是:这不就是个电商系统嘛。可等到把需求文档一条条过完,才发现茶叶这类目背后藏着一堆商品逻辑——品类分级、产地、工艺、年份、批次口感,甚至同一款茶换个山头味道就完全不一样。…

📰

Milvus向量数据库实战:为AI助手打造长期记忆库

不知道你有没有遇到过这种情况:明明前一天跟AI助手聊过某件事,第二天再问它,它却跟失忆了一样,完全不记得对话内容。这种感觉就像在养一条金鱼——七秒的记忆,鱼缸转一圈就忘了自己刚才游到过哪。给AI加“长期记忆”&a…

📰

YOLOv10在飞机蒙皮表面缺陷检测中的实战应用与调优详解

前阵子帮某维修单位做了一套飞机蒙皮缺陷自动检测系统,核心就是拿 YOLOv10 把划痕、凹坑、腐蚀、裂纹这些表面缺陷从蒙皮照片里自动找出来,支持图片文件、视频文件和摄像头实时三种输入方式。这套东西做下来,我对“表面缺陷检测到底难在哪”有…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬