C# ITextSharp中的PdfCopyForms导致堆栈溢出错误

C# ITextSharp中的PdfCopyForms导致堆栈溢出错误,c#,itextsharp,stack-overflow,C#,Itextsharp,Stack Overflow,在这种方法中,我试图从一个PDF文档中获取输入字段,将它们粘贴到另一个文档中,并将结果打印为PDF文件。结果将是一个新的PDF文件,其中包含第一个PDF的输入字段和第二个PDF的静态内容 我编写了一些我认为可以执行此任务的代码,但每次执行“copier.close()”时都会遇到StackOverflow错误。这是它抛出的错误: An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.

在这种方法中,我试图从一个PDF文档中获取输入字段,将它们粘贴到另一个文档中,并将结果打印为PDF文件。结果将是一个新的PDF文件,其中包含第一个PDF的输入字段和第二个PDF的静态内容

我编写了一些我认为可以执行此任务的代码,但每次执行“copier.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位于此处:

我用于secondSourcePath变量的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中。你知道如何解决这个问题吗?

我用iText和Java复制了这个问题;同样的问题也发生在这里,所以原因很可能是相同的

PdfCopyForms
在内部使用
PdfCopyFormsImp
,它是从
PdfCopyFieldsImp
派生的。后一个类提供了执行字段和表单复制的繁重工作的基本方法,其中包括
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
的对象引用标记为已访问,并返回以前是否访问过该对象

至少对于所有
PdfReader
实例的引用,在
已访问的
映射中有一个条目,它是这样做的,来自
PdfReader
实例的引用,如果没有这样的条目,则总是声称尚未访问(
返回false
)。因此,在多次访问的情况下,后一类读者的参考文献不会被视为已访问

PdfReader
实例在
已访问的
映射中仅在一个代码位置获取条目:只有使用
addDocument添加到副本的读卡器才能获取该条目

使用
PdfCopyForms
将表单字段从一个文档添加到另一个PDF文档中,显然不会对要复制表单的读者使用
addDocument
,而是使用
copyDocumentFields
。因此,循环预防在这里不起作用

通过为复制表单的读卡器在
已访问的
映射中添加条目,可以防止堆栈溢出。我是在
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);
}

免责声明:我没有检查
PdfCopyForms
在此次更改后是否完全按照要求工作。我只是用Java对它进行了测试,只发现不再发生堆栈溢出,OP用例中生成的PDF看起来还可以;同样的问题也发生在这里,所以原因很可能是相同的

PdfCopyForms
在内部使用
PdfCopyFormsImp
,它是从
PdfCopyFieldsImp
派生的。后一个类提供了执行字段和表单复制的繁重工作的基本方法,其中包括
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
的对象引用标记为已访问,并返回以前是否访问过该对象

至少对于所有
PdfReader
实例的引用,在
已访问的
映射中有一个条目,它是这样做的,来自
PdfReader
实例的引用,如果没有这样的条目,则总是声称尚未访问(
返回false
)。因此,在多次访问的情况下,后一类读者的参考文献不会被视为已访问

PdfReader
实例在
已访问的
映射中仅在一个代码位置获取条目:只有使用
addDocument添加到副本的读卡器才能获取该条目

使用
PdfCopyForms
将表单字段从一个文档添加到另一个PDF文档中,显然不会对要复制表单的读者使用
addDocument
,而是使用
copyDocumentFields
。因此,循环预防在这里不起作用

通过为复制表单的读卡器在
已访问的
映射中添加条目,可以防止堆栈溢出。我是在
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);
}

免责声明:我没有检查
PdfCopyForms
在此次更改后是否完全按照要求工作。我只是用Java对它进行了测试,只发现不再发生堆栈溢出,OP用例中生成的PDF看起来还可以。

我使用iText和Jav重现了这个问题