2015-12-30 6 views
2

Я использую iTextSharp для извлечения SignatureNames из PDF. Я столкнулся с проблемами (чрезмерной медлительностью) доступа к AcroFiels большого размера и многим страницам PDF (~ 40 МБ и ~ 5000 страниц).Медленный доступ к AcroFields (iTextSharp)

Вот мой фрагмент кода:

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 вещи?

Большое спасибо.

+1

Пожалуйста, объясните, что именно вы имеете в виду * чрезмерная медлительность *. И если возможно, поделитесь ссылкой на образец PDF для анализа. – mkl

+0

Для _очередной slowness_ я имею в виду около ** 20-60 минут ** времени ожидания как отладки в VS как в выпуске на производственной машине. Я пытаюсь найти PDF-файл, который я могу использовать здесь, потому что единственный, у кого есть проблемы, содержит разумные данные клиента. – alessandrotoro

+0

Хорошо, это ** является ** чрезмерной медлительностью. :) Я еще не испытал такую ​​медлительность с iText (Sharp). Таким образом, вполне вероятно, что что-то особенное в этих pdf-файлах. – mkl

ответ

1

В комментариях гипотеза пришла, что чрезмерная медлительность связано с тем, что IText (Sharp) во время инициализации коллекции поля в AcroFields инстанции не только проверяет поля ссылки в Каталог ->AcroForm ->Поля, но также (в действительности) от ANNOTS всех страниц документа.

К счастью, эта инициализация не выполняется в конструкторе AcroFields, поэтому мы можем ввести извлеченную полевую коллекцию без проверки всех страниц.

Следующий метод представляет собой копию внутреннего метода AcroFieldsFill (который отвечает за ленивую инициализацию) с удалением страницы и с доступом к скрытым членам, включенным с помощью отражения. Его можно использовать для проверки гипотезы.

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); 
    ... 

При использовании этого метода сокращает время значительно (в то же время при подаче желаемых полей), то гипотеза подтверждена.


Глядя на код одного признает, что это не правильно ходить структуру поля: Поля могут быть организованы иерархически, но код учитывает только первые элементы уровня. Однако этого достаточно для первого теста гипотезы, упомянутой выше.

Смежные вопросы