【问题标题】:Can iTextSharp generate PDF with JPEG images that are multi-stage filtered (both DCTDecode and FlateDecode)?iTextSharp 可以使用经过多阶段过滤(DCTDecode 和 FlateDecode)的 JPEG 图像生成 PDF 吗?
【发布时间】:2014-03-24 09:14:58
【问题描述】:

最近我的任务是减少从空白办公文档生成的 PDF 的文件大小。

图像大多是空白的,但它们有各种公司信头(彩色)、边框和页脚。有些是由软件生成的(因此具有非常干净的像素),有些是由桌面扫描仪扫描的。

作为“空白”,我的意思是页面的中心部分(距每个边距两英寸)将是绝对空白和白色的。

我的老板想让这些 PDF 保持彩色,但不介意让它们变得更模糊,只要它们不太难看。

我已经测试了很多文件缩减方案:

  • 不同的颜色压缩方法(FlateDecode、LZWDecode、DCTDecode...)
  • 不同的 JPEG 质量设置
  • 减少 JPEG 的像素宽度和高度,仅在显示 PDF 时将其拉伸。
  • 将图像内容切割成更小的图像块。

到目前为止,我发现第三种方法(减少像素尺寸)比减少 JPEG 质量设置(例如,将图像缩小 50% 而不是将质量从 50 降低到 20)更有效

但是,在我们从其他公司收集的一些示例 PDF 中,让我无法理解的一种方法是,其中一些具有 多阶段过滤的 JPEG 图像。我的意思是:

  • 过滤器名称是一个由两个组成的数组:/DCTDecode、/FlateDecode
  • 生成 PDF 时,先应用 JPEG 压缩,然后再压缩。在查看 PDF 时,首先将数据膨胀,然后将 JPEG 解压缩为像素。

我将解压的第一步(FlateDecode)应用于多级过滤数据,得到原始 JPEG 数据。我使用十六进制查看器检查JPEG数据,发现在JPEG图像的空白区域中,大部分字节都是重复的模式。这就解释了为什么在 JPEG 文件之上应用二次压缩是有利的 - 如果只有一个人知道 JPEG 图像大部分是空白的。

显然,这些 PDF 也是由 iText 创建的。但是,我不清楚是否有 iTextSharp.text.Image 类的实例支持这种两级过滤的组合。

如果 iText 没有内置支持创建这样的两阶段压缩图像,如果我处理两阶段压缩并使用类似 PdfImageObject 的东西,是否可以插入图像?

【问题讨论】:

  • 您看到 2 级压缩有哪些改进?即使有重复的空白块,JPEG 的熵也相当高。
  • 很遗憾,我没有时间编写示例,但我可以向您推荐一个可以帮助您的示例。看看这个例子中图像的创建方式:itextpdf.com/sandbox/tables/AddSpotColorImage 您可以从Image 创建一个PdfImage,然后更改该对象的键和数据(并非微不足道,但并非不可能) .我将在我的 TODO 列表中添加一个项目,看看是否可以添加选项以将 /FlateDecode 过滤器添加到下一个版本的图像中。
  • 是的,使用PdfImageObject 并按照我的描述替换流也可以。我的评论是假设您希望在第一次(而不是第二次)中应用压缩。我(仍然)没有时间做一个例子,但我会在今天结束之前用两种可能的方法写一个答案。
  • 我没有忘记这个问题,但是在尝试编写最简洁的工作示例时,我发现了 iText 中的一个错误。我目前正在修复它;-)
  • 这已在主干(SVN 存储库)中修复。

标签: pdf itextsharp jpeg image-compression


【解决方案1】:

我创建了两个示例,一个适用于当前 iText 版本,一个仅适用于下一个 iText 版本(因为我添加了一些功能)。

这是最简单的方法(但只能从 iText 5.5.1 开始使用):

Image img = Image.getInstance("some.jpg");
img.setCompressionLevel(PdfStream.BEST_COMPRESSION);

当您将 JPEG 传递给 iText 时,过滤器将是 /DCTDecode。我重用了现有的setCompressionLevel() 方法,使流也会被压缩。您将在过滤器中有两个条目:/FlateDecode/DCTDecode。这显示在 FlateCompressJPEG1Pass 例子。

由于这需要一个尚未正式发布的 iText 版本,我还创建了 FlateCompressJPEG2Passes 示例。

PdfReader reader = new PdfReader(src);
// We assume that there's a single large picture on the first page
PdfDictionary page = reader.getPageN(1);
PdfDictionary resources = page.getAsDict(PdfName.RESOURCES);
PdfDictionary xobjects = resources.getAsDict(PdfName.XOBJECT);
PdfName imgName = xobjects.getKeys().iterator().next();
PRStream imgStream = (PRStream)xobjects.getAsStream(imgName);
imgStream.setData(PdfReader.getStreamBytesRaw(imgStream), true);
PdfArray array = new PdfArray();
array.add(PdfName.FLATEDECODE);
array.add(PdfName.DCTDECODE);
imgStream.put(PdfName.FILTER, array);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();

此代码示例对现有文档进行后处理,它需要更多代码并且更容易出错:在这个简短的 sn-p 中,我假设有一个 XObject 并且这个单个对象是一个图像。您的 PDF 可能并非如此。

【讨论】:

    猜你喜欢
    • 2015-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 1970-01-01
    • 2022-12-10
    • 2011-07-27
    • 2016-02-14
    相关资源
    最近更新 更多