使用pdfStamper进行多次传递可减小文件大小

本文关键字:文件大小 pdfStamper 使用 | 更新日期: 2023-09-27 18:28:54

我正在使用iTextSharp填写PDF上的一些表单字段。

PdfReader pdfReader = new PdfReader(templateFile);
//http://stackoverflow.com/questions/17852902/disable-extended-features-with-itextsharp Prevent annoying "extended features disabled" warning in Adobe Reader
pdfReader.RemoveUsageRights();
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(fileName, FileMode.Create), PdfWriter.VERSION_1_7);
pdfStamper.SetFullCompression();
pdfStamper.Writer.CompressionLevel = PdfStream.BEST_COMPRESSION;
AcroFields pdfFormFields = pdfStamper.AcroFields;
// set form pdfFormFields
pdfFormFields.SetField("field1", "value1");
pdfFormFields.SetField("field2", "value2");
pdfFormFields.SetField("field3", "value3");
//etc
pdfStamper.FormFlattening = false;
// close the pdf
pdfStamper.Close();

填写完PDF字段后,我不会立即压平表单,因此可以根据需要进行手动更改。一旦进行了手动更改,我就会打开PDF,设置最大压缩,压平表单,保存&关闭文档。

//Move the original file so I can recreate it without editable form fields
string tempFileName = filename + ".temp";
File.Move(filename, tempFileName);
using (PdfReader pdfReader = new PdfReader(tempFileName))
{
    using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(filename, FileMode.Create), PdfWriter.VERSION_1_7))
    {
        pdfStamper.SetFullCompression();
        pdfStamper.Writer.CompressionLevel = PdfStream.BEST_COMPRESSION;
        // flatten the form to remove editting options
        pdfStamper.FormFlattening = true;
        pdfStamper.Close();
    }
    pdfReader.Close();
}
//Delete the original temp file
File.Delete(tempFileName);

我第一次直接运行上面的代码来压缩和压平PDF时,每个文件的大小都略有减小,从300KB减小到256KB。但是,如果我再次运行上面的代码,文件大小会大大减少,从256KB减少到95KB。后续运行不会进一步更改文件大小。我的问题是,如何让iTextSharp第一次输出最小的文件大小?

编辑

从使表单变平的块中删除压缩代码会得到相同的结果,尽管最终大小稍大,为105KB。

使用pdfStamper进行多次传递可减小文件大小

这种行为的原因很简单:

  • 将文档加载到PdfReader中时,将立即丢弃未使用的对象。(如果您在部分模式下工作,则不会,但您不这样做。)
  • 当您关闭PdfStanper时,它会从它标记的PdfReader中复制所有对象,并添加一些自己的、尚未写入的信息

因此,

  • 在您的第一次传递中,阅读器在加载PDF时保留用于表单字段的所有对象,因为那时它们仍在使用。压模然后复制所有与表单字段相关的对象,即使它们由于变平而不再使用
  • 在您的第二步中,阅读器在加载PDF时会删除以前用于表单字段的所有对象,因为表单已经不存在了。因此,压模无法再复制它们。在这个过程中不会发生压扁,因为没有任何形式可以压扁了

第一次传递中的小尺寸减少可能是由于源文件中已经有少量未使用的杂散对象,或者iTest(Sharp)进行了更好的压缩。

第二次传递中的大尺寸减小肯定是由于丢弃了与表单字段相关的对象。

关于你的问题

如何让iTextSharp在第一次时输出最小的文件大小

你不能。压模通常不能简单地丢弃与表单相关的对象,因为它们也可能被不同的对象使用。它甚至无法检查这种用途,因为之前执行的其他压模操作可能已经创建了新的PDF对象,这些对象确实引用了这些有问题的对象,但这些新生成的PDF对象早就被写入输出,压模无法再访问它们。

不过,您可以通过使用MemoryStream作为第1步的输出和第2步的输入来防止中间PDF出现在光盘上。


如果你想知道PdfStamper为什么不把那些新创建的对象保存在内存中,以便以后检查未使用的对象:iText(Sharp)是在考虑服务器应用程序和大PDF的情况下创建的;在这种情况下,应该尽早写入数据并释放内存。