ITextSharp中的PdfCopyForms导致堆栈溢出错误
本文关键字:堆栈 栈溢出 错误 中的 PdfCopyForms ITextSharp | 更新日期: 2023-09-27 18:05:36
在这种方法中,我试图从一个PDF文档中获取输入字段,将它们粘贴到另一个文档中,并将结果打印为PDF文件。结果将是一个新的PDF文件,其中包含第一个PDF的输入字段和第二个PDF的静态内容。
我写了一些我认为会执行这个任务的代码,但是每次执行"copy .close()"时,我都会遇到StackOverflow错误。这是它抛出的错误:
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
这是代码:
public static void AddFormFieldsFromSource(string sourcePath, string secondSourcePath, string targetPath) {
lock (syncLock) {
PdfReader.unethicalreading = true;
PdfReader readerMain = new PdfReader(sourcePath);
FileStream stream = new FileStream(targetPath, FileMode.Create, FileAccess.Write);
PdfCopyForms copier = new PdfCopyForms(stream);
PdfReader secondSourceReader = new PdfReader(secondSourcePath);
copier.AddDocument(secondSourceReader);
copier.CopyDocumentFields(readerMain);
copier.Close();
secondSourceReader.Close();
}
}
源路径是我获取输入字段的地方,第二个源路径是我获取静态内容的地方。
我用于SourcePath变量的PDF位于这里:https://www.dropbox.com/s/qcc6ug8oohqvmca/primarytwopages2.pdf
我用于secondSourcePath变量的PDF位于这里:https://www.dropbox.com/s/kx2rlhmizh46hl7/secondarytwopages.pdf
另外,我使用的是ITextSharp 5.5.0版本。
知道为什么它抛出StackOverflow错误吗?我在代码中没有做任何递归调用。我的第一个猜测是,我试图做这个任务是错误的。另一种可能是ITextSharp有bug。
更新:我下载了ITextSharp最新版本(5.5.1)的源代码,构建了一个dll以便我可以调试,然后在我的代码中引用该dll。这个方法中的PdfIndirectReference类中出现了堆栈溢出错误:
public class PdfIndirectReference : PdfObject {
....
internal PdfIndirectReference(int type, int number, int generation) : base(0, new StringBuilder().Append(number).Append(' ').Append(generation).Append(" R").ToString()) {
this.number = number;
this.generation = generation;
}
在dll代码的调用堆栈中,我发现它递归地反复调用
中的方法。itextsharp.text.pdf.PdfCopyFieldsImp.Propagate()。
这一定是发生堆栈溢出的原因。
所以,它不会发生在我的代码中,而是发生在dll中。你知道怎么解决这个问题吗?
我用ittext和Java重现了这个问题;这里也出现了同样的问题,所以很可能原因是相同的。
PdfCopyForms
内部使用由PdfCopyFieldsImp
衍生而来的PdfCopyFormsImp
。后一个类提供了执行字段和表单复制的繁重工作的基本方法,其中propagate
是OP在堆栈溢出发生时多次在调用堆栈中发现的。
与观察到的堆栈溢出留下的印象相反,PdfCopyFieldsImp
确实具有通过标记已经访问的对象来防止无限循环的机制:
/**
* Sets a reference to "visited" in the copy process.
* @param ref the reference that needs to be set to "visited"
* @return true if the reference was set to visited
*/
protected boolean setVisited(PRIndirectReference ref) {
IntHashtable refs = visited.get(ref.getReader());
if (refs != null)
return refs.put(ref.getNumber(), 1) != 0;
else
return false;
}
此方法同时将来自某个PdfReader
的对象引用标记为已访问,并返回该对象之前是否被访问过。
至少对于所有在visited
映射中有一个表项的PdfReader
实例的引用是这样做的,没有这个表项的PdfReader
实例的引用总是被声明尚未访问(return false
)。因此,在多次访问的情况下,这些后一种读者的引用不被认为是访问过的!
PdfReader
实例只能在一个代码位置获得visited
映射中的条目:只有使用addDocument
添加到副本的读取器才能获得该条目。
使用PdfCopyForms
将表单字段从一个文档添加到另一个PDF,显然不使用addDocument
来复制表单,而是使用copyDocumentFields
。因此,循环预防在这里不起作用。
通过在visited
映射中为复制表单的阅读器添加一个条目,可以防止Stack Overflow。我在PdfCopyFormsImp.copyDocumentFields
public void copyDocumentFields(PdfReader reader) throws DocumentException {
if (!reader.isOpenedWithFullPermissions())
throw new IllegalArgumentException(MessageLocalization.getComposedMessage("pdfreader.not.opened.with.owner.password"));
if (readers2intrefs.containsKey(reader)) {
reader = new PdfReader(reader);
}
else {
if (reader.isTampered())
throw new DocumentException(MessageLocalization.getComposedMessage("the.document.was.reused"));
reader.consolidateNamedDestinations();
reader.setTampered(true);
}
reader.shuffleSubsetNames();
readers2intrefs.put(reader, new IntHashtable());
visited.put(reader, new IntHashtable()); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
fields.add(reader.getAcroFields());
updateCalculationOrder(reader);
}
在iTextSharp中,类似的变化将在PdfCopyFormsImp.CopyDocumentFields
:
virtual public void CopyDocumentFields(PdfReader reader) {
if (!reader.IsOpenedWithFullPermissions)
throw new BadPasswordException(MessageLocalization.GetComposedMessage("pdfreader.not.opened.with.owner.password"));
if (readers2intrefs.ContainsKey(reader)) {
reader = new PdfReader(reader);
}
else {
if (reader.Tampered)
throw new DocumentException(MessageLocalization.GetComposedMessage("the.document.was.reused"));
reader.ConsolidateNamedDestinations();
reader.Tampered = true;
}
reader.ShuffleSubsetNames();
readers2intrefs[reader] = new IntHashtable();
visited[reader] = new IntHashtable(); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
fields.Add(reader.AcroFields);
UpdateCalculationOrder(reader);
}
免责声明:我没有检查PdfCopyForms
是否完全按照要求进行此更改。我只是在Java中测试了它,只观察到不再发生堆栈溢出,并且OP用例中的PDF结果看起来很好。