使用pdfStamper进行多次处理可以减小文件大小。

3
我正在使用iTextSharp来填写PDF上的一些表单字段。
PdfReader pdfReader = new PdfReader(templateFile);
//https://dev59.com/dGMm5IYBdhLWcg3wHMN0 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。

1
请明确一下,当您说“我第一次运行这段代码”时,是指您正在多次运行的第二个代码块吗? - Chris Haas
1
我猜测问题的原因是,在第一遍处理中,字段使用的资源仍嵌入在PDF中(只有在关闭时扁平化),因此那些资源会被引用很长时间,并且仅在第二遍处理中才被删除。你能分享一个填充好的PDF以重现这个问题吗? - mkl
@ChrisHaas,没错。对PDF文件进行第二个代码块的多次处理会导致这种情况发生。 - Preston S
1个回答

1
这种行为的原因很简单:
当你将文档加载到 PdfReader 中时,未使用的对象会立即被删除。(如果你在部分模式下工作就不是这样了。)
当你关闭 PdfStanper 时,它会从它印章的 PdfReader 复制所有对象并添加一些自己的尚未编写的信息。
因此,
在第一次通过时,阅读器在加载 PDF 时保留了用于表单字段的所有对象,因为它们仍然在使用。尽管已经压平,但印章器仍会复制所有这些与表单字段相关的对象。
在第二遍通过时,读者删除了以前用于表单字段的所有对象,因为表单已经不存在了。因此,印章器不能再复制它们。在这个过程中不会进行压平,因为没有表单需要压平了。
第一次通过中的小尺寸减少可能是源文件中一些偶然的未使用对象或 iTest(Sharp) 更好的压缩所致。
第二次通过中的大尺寸减少明显是由于删除了与表单字段相关的对象。
关于你的问题

如何使iTextSharp第一次输出最小的文件大小?

你做不到。通常情况下,印章器不能简单地删除与表单相关的对象,因为它们可能也被其他对象使用。它甚至不能检查这样的用法,因为之前执行的其他印章操作可能已经创建了新的PDF对象,这些对象引用了那些问题对象,但是那些新生成的PDF对象已经被写入输出中,而且印章器无法再访问它们。

不过,你可以通过在第一遍使用MemoryStream作为输出并在第二遍使用它作为输入来避免中间文件出现在磁盘上。


如果你想知道为什么PdfStamper不会将这些新创建的对象保留在内存中以便稍后检查未使用的对象:iText(Sharp)是专为服务器应用和大型PDF而开发的;在这种情况下,应尽早写入数据并释放它们的内存。

要使用内存流,请按照mkl在此处的答案操作(http://stackoverflow.com/questions/25415350/returning-itextsharp-pdf-as-memorystream-causes-streamnotsupported/25427872#25427872)。 - Preston S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接