【问题标题】:PdfCopyForms in ITextSharp causing a Stack Overflow errorITextSharp 中的 PdfCopyForms 导致堆栈溢出错误
【发布时间】:2014-05-27 15:34:20
【问题描述】:

在这种方法中,我试图从一个 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 位于此处: https://www.dropbox.com/s/qcc6ug8oohqvmca/primarytwopages2.pdf

我用于 secondSourcePath 变量的 PDF 位于此处: https://www.dropbox.com/s/kx2rlhmizh46hl7/secondarytwopages.pdf

另外,另一方面,我使用的是 ITextSharp 5.5.0 版。

知道为什么会抛出 StackOverflow 错误吗?我没有在我的代码中进行任何递归调用。我的第一个猜测是我试图错误地执行此任务。另一种可能性是 ITextSharp 可能存在错误。

更新:我将源代码下载到 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 中。知道如何解决这个问题吗?

【问题讨论】:

  • 你能提供有问题的pdf吗?
  • 当然。给我几分钟,我会提供一个保管箱链接。
  • K,现在我将 Dropbox 链接放在问题文本中。他们导致pdf。感谢您的意见。
  • 我明天看看。

标签: c# itextsharp stack-overflow


【解决方案1】:

我使用 iText 和 Java 重现了该问题;这里出现同样的问题,所以很可能原因是一样的。

PdfCopyForms 内部使用派生自PdfCopyFieldsImpPdfCopyFormsImp。后一个类提供了执行繁重的字段和表单复制的基本方法,其中包括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 映射中为从中复制表单的阅读器添加一个条目,可以防止堆栈溢出。我是在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 看起来没问题。

【讨论】:

  • 你太棒了。当我开始工作时,我会立即检查这个解决方案。然后我会接受它作为答案(可能是因为 Itext 和 itextsharp 是相同的)
  • ITextSharp 修改工作顺利!谢谢!
  • 谢谢迈克尔!我已经为这个问题制作了一张内部票。我已经用你的答案更新了票。修复将在下一个版本中。
  • 谢谢。但请注意,我没有进行深入分析,因此无法说明提议的更改是否有任何不良副作用。
猜你喜欢
  • 2011-02-08
  • 1970-01-01
  • 2015-05-21
  • 2014-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多