c# - 对 AcroFields (iTextSharp) 的访问速度缓慢

标签 c# itext acrofields

我正在使用 iTextSharp 从 PDF 中提取 SignatureNames。 我在访问大尺寸和多页 PDF(约 40MB 和约 5000 页)的 AcroFiels 时遇到问题(速度过慢)。

这是我的代码片段:

using iTextSharp.text.pdf;

private static List<byte[]> GetSignsFromPDF(string filePath)
{
    var result = new List<byte[]>();
    var randomAccessFileOrArray = new RandomAccessFileOrArray(filePath);
    var reader = new PdfReader(randomAccessFileOrArray, null);
    var fields = reader.AcroFields;

    if (fields == null)
    {
        return result;
    }

    var signatureNames = fields.GetSignatureNames();
    signatureNames.Sort();

    foreach (string name in signatureNames)
    {
        var sigDict = fields.GetSignatureDictionary(name);
        var contents = sigDict.GetAsString(PdfName.CONTENTS);

        if (contents != null)
        {
            result.Add(contents.GetOriginalBytes());
        }
    }

    return result;
}

有一种更智能/更快的方式来访问 AcroFields 还是我应该等待 iTextSharp 的东西?

非常感谢。

最佳答案

在评论中,有人猜测 速度过慢 不仅是由于在 AcroFields 实例中初始化字段集合期间 iText(Sharp) 造成的检查 Catalog -> AcroForm -> Fields 中引用的字段,但也(实际上是最重要的)来自 ANNOTS所有文档页面。

幸运的是,此初始化不会在 AcroFields 构造函数中进行,因此我们可以注入(inject)检索到的字段集合,而无需检查所有页面。

以下方法是内部 AcroFields 方法 Fill(负责延迟初始化)的副本,其中删除了页面遍历并启用了对隐藏成员的访问通过反射。可以用来验证猜想。

void fill(PdfReader reader, AcroFields acroFields)
{
    IDictionary<string, AcroFields.Item> fields = new LinkedDictionary<string, AcroFields.Item>();
    PdfDictionary top = (PdfDictionary)PdfReader.GetPdfObjectRelease(reader.Catalog.Get(PdfName.ACROFORM));
    if (top == null)
        return;
    PdfBoolean needappearances = top.GetAsBoolean(PdfName.NEEDAPPEARANCES);
    if (needappearances == null || !needappearances.BooleanValue)
        acroFields.GenerateAppearances = true;
    else
        acroFields.GenerateAppearances = false;
    PdfArray arrfds = (PdfArray)PdfReader.GetPdfObjectRelease(top.Get(PdfName.FIELDS));
    if (arrfds == null || arrfds.Size == 0)
        return;

    System.Reflection.FieldInfo valuesField = typeof(AcroFields.Item).GetField("values", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    System.Reflection.FieldInfo widgetsField = typeof(AcroFields.Item).GetField("widgets", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    System.Reflection.FieldInfo widgetRefsField = typeof(AcroFields.Item).GetField("widget_refs", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    System.Reflection.FieldInfo mergedField = typeof(AcroFields.Item).GetField("merged", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    System.Reflection.FieldInfo pageField = typeof(AcroFields.Item).GetField("page", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    System.Reflection.FieldInfo tabOrderField = typeof(AcroFields.Item).GetField("tabOrder", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    for (int j = 0; j < arrfds.Size; ++j)
    {
        PdfDictionary annot = arrfds.GetAsDict(j);
        if (annot == null)
        {
            PdfReader.ReleaseLastXrefPartial(arrfds.GetAsIndirectObject(j));
            continue;
        }
        if (!PdfName.WIDGET.Equals(annot.GetAsName(PdfName.SUBTYPE)))
        {
            PdfReader.ReleaseLastXrefPartial(arrfds.GetAsIndirectObject(j));
            continue;
        }
        PdfArray kids = (PdfArray)PdfReader.GetPdfObjectRelease(annot.Get(PdfName.KIDS));
        if (kids != null)
            continue;
        PdfDictionary dic = new PdfDictionary();
        dic.Merge(annot);
        PdfString t = annot.GetAsString(PdfName.T);
        if (t == null)
            continue;
        String name = t.ToUnicodeString();
        if (fields.ContainsKey(name))
            continue;
        AcroFields.Item item = new AcroFields.Item();
        fields[name] = item;
        ((List<PdfDictionary>)valuesField.GetValue(item)).Add(dic); // item.AddValue(dic);
        ((List<PdfDictionary>)widgetsField.GetValue(item)).Add(dic); // item.AddWidget(dic);
        ((List<PdfIndirectReference>)widgetRefsField.GetValue(item)).Add(arrfds.GetAsIndirectObject(j)); //item.AddWidgetRef(arrfds.GetAsIndirectObject(j)); // must be a reference
        ((List<PdfDictionary>)mergedField.GetValue(item)).Add(dic); // item.AddMerged(dic);
        ((List<int>)pageField.GetValue(item)).Add((int)-1); // item.AddPage(-1);
        ((List<int>)tabOrderField.GetValue(item)).Add((int)-1); // item.AddTabOrder(-1);
    }

    System.Reflection.FieldInfo fieldsField = typeof(AcroFields).GetField("fields", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    fieldsField.SetValue(acroFields, fields);
}

应该尽早为 AcroFields 实例调用它,例如:

using (PdfReader reader = new PdfReader(file))
{
    AcroFields acroFields = reader.AcroFields;
    fill(reader, acroFields);
    ...

如果使用此方法大大减少了时间(同时提供所需的字段),则该猜想得到证实。


查看代码,您会发现它没有正确遍历字段结构:字段可以分层排列,但代码仅考虑第一级元素。不过,对于上面提到的猜想的第一次测试来说,它应该足够了。

关于c# - 对 AcroFields (iTextSharp) 的访问速度缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34528357/

相关文章:

c# - Caliburn.Micro Binding 似乎无法在 View 模型中解析

java - 如何使用嵌入式字体通过 iText(DefaultFontMapper?)调用 Graphics2D.drawString(...)

java - 使用 pdfbox 调用 acroform.getFields() 时获取 null

pdf - 使用 iText 更改现有 PDF 中的 AcroFields 顺序?

c# - 如果一个字段为空,我如何用 Linq 替换一个值?

c# - 从普通 C# 类调用 SignalR Client 方法

c# - 电子邮件正文格式问题

java - 类 "org.bouncycaSTLe.asn1.ASN1ObjectIdentifier"的签名者信息与同一包中其他类的签名者信息不匹配

c# - 使用 itextSharp 从 pdf 中提取文本会更改数字

javascript - 自动在字符之间添加空格