如何用iText与Java将PDF内图像转为单色调/二值图及替换图像
用iText 7 + Java将PDF内的RGB/灰度图像转为二值图像
我来帮你梳理下实现思路和具体方案,结合你已经在用的工具和纯iText两种方式来解决:
方案一:结合ImageMagick处理(推荐,精准度更高)
既然你已经能通过ImageMagick提取并转换图像,那可以把这个流程整合到Java代码里,步骤如下:
- 遍历PDF中的图像资源,提取原图像的字节流
- 调用ImageMagick的
convert命令完成二值化转换 - 将处理后的二值图像重新封装为PDF可识别的
PdfImageXObject - 替换PDF中原有的图像引用
示例代码片段:
// 打开PDF文档 PdfDocument pdfDoc = new PdfDocument(new PdfReader("input.pdf"), new PdfWriter("output.pdf")); // 遍历每一页的图像资源 for (int pageNum = 1; pageNum <= pdfDoc.getNumberOfPages(); pageNum++) { PdfPage page = pdfDoc.getPage(pageNum); PdfDictionary resources = page.getResources(); PdfDictionary xObjects = resources.getAsDictionary(PdfName.XObject); if (xObjects != null) { for (PdfName imgName : xObjects.keySet()) { PdfObject obj = xObjects.get(imgName); if (obj.isIndirectReference()) { PdfStream imgStream = pdfDoc.getPdfObject(obj.getIndirectReference()).getAsStream(); if (PdfName.Image.equals(imgStream.getAsName(PdfName.Subtype))) { // 提取原图像字节 PdfImageXObject originalImg = new PdfImageXObject(imgStream); byte[] imgBytes = originalImg.getImageBytes(); Files.write(Paths.get("temp-original.png"), imgBytes); // 调用ImageMagick执行二值化(阈值可根据实际调整) ProcessBuilder pb = new ProcessBuilder( "convert", "temp-original.png", "-threshold", "50%", "temp-binary.png" ); pb.inheritIO().start().waitFor(); // 生成新的PDF图像对象并替换原图像 PdfImageXObject newBinaryImg = new PdfImageXObject(ImageDataFactory.create("temp-binary.png")); xObjects.put(imgName, newBinaryImg.getPdfObject()); } } } } } pdfDoc.close(); // 清理临时文件 Files.deleteIfExists(Paths.get("temp-original.png")); Files.deleteIfExists(Paths.get("temp-binary.png"));
方案二:纯Java/iText实现二值化(无需外部工具)
如果不想依赖ImageMagick,可以用Java自带的图像API完成二值化,步骤如下:
- 从
PdfImageXObject获取BufferedImage对象 - 对图像进行二值化处理(固定阈值或自适应阈值)
- 将处理后的图像转为PDF可识别的格式,替换原图像
示例代码片段:
PdfDocument pdfDoc = new PdfDocument(new PdfReader("input.pdf"), new PdfWriter("output.pdf")); for (int pageNum = 1; pageNum <= pdfDoc.getNumberOfPages(); pageNum++) { PdfPage page = pdfDoc.getPage(pageNum); PdfDictionary resources = page.getResources(); PdfDictionary xObjects = resources.getAsDictionary(PdfName.XObject); if (xObjects != null) { for (PdfName imgName : xObjects.keySet()) { PdfObject obj = xObjects.get(imgName); if (obj.isIndirectReference()) { PdfStream imgStream = pdfDoc.getPdfObject(obj.getIndirectReference()).getAsStream(); if (PdfName.Image.equals(imgStream.getAsName(PdfName.Subtype))) { PdfImageXObject originalImg = new PdfImageXObject(imgStream); BufferedImage originalBufImg = originalImg.getBufferedImage(); // 二值化处理:使用Java自带的TYPE_BYTE_BINARY格式 BufferedImage binaryBufImg = new BufferedImage( originalBufImg.getWidth(), originalBufImg.getHeight(), BufferedImage.TYPE_BYTE_BINARY ); Graphics2D g2d = binaryBufImg.createGraphics(); g2d.drawImage(originalBufImg, 0, 0, null); g2d.dispose(); // 将二值图像转为PDF图像对象 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(binaryBufImg, "png", baos); ImageData binaryImgData = ImageDataFactory.create(baos.toByteArray()); PdfImageXObject newBinaryImg = new PdfImageXObject(binaryImgData); // 替换原图像 xObjects.put(imgName, newBinaryImg.getPdfObject()); } } } } } pdfDoc.close();
额外注意事项
- 高DPI图像的二值化阈值可以根据实际情况调整,固定阈值效果不好的话,可以尝试Otsu自适应阈值算法来优化
- iText 7.1.x版本对图像处理的兼容性不错,如果你遇到奇怪的问题,升级到最新稳定版(比如7.1.17+)会更稳妥
- 替换图像时,原图像的位置、尺寸等属性会自动保留,不需要额外处理
内容的提问来源于stack exchange,提问作者Schloppe
相关产品推荐
相关产品推荐

