Попытка применить изменения приводит к исключению

Я следовал за шагами, чтобы создать аннотации и применить редактирование, используя iText 5.5.9. Вот мой код:

using (var stamper = new PdfStamper(pdfReader, new FileStream(newFilePath, FileMode.Create)))
{
    // Redact the values.
    var pdfAnot1 = new PdfAnnotation(stamper.Writer, new Rectangle(165f, 685f, 320f, 702f));
    pdfAnot1.Title = "First Page";
    pdfAnot1.Put(PdfName.SUBTYPE, PdfName.REDACT);
    pdfAnot1.Put(PdfName.IC, new PdfArray(new[] { 0f, 0f, 0f }));
    pdfAnot1.Put(PdfName.OC, new PdfArray(new[] { 1f, 0f, 0f })); // red outline
    stamper.AddAnnotation(pdfAnot1, 1);
    for (var i = 1; i <= pdfReader.NumberOfPages; i++)
    {
        var pdfAnot2 = new PdfAnnotation(stamper.Writer, new Rectangle(220f, 752f, 420f, 768f));
        pdfAnot2.Title = "Header";
        pdfAnot2.Put(PdfName.SUBTYPE, PdfName.REDACT);
        pdfAnot2.Put(PdfName.IC, new PdfArray(new[] { 0f, 0f, 0f }));
        pdfAnot2.Put(PdfName.OC, new PdfArray(new[] { 1f, 0f, 0f })); // red outline
        stamper.AddAnnotation(pdfAnot2, i);
    }

    var cleaner = new PdfCleanUpProcessor(stamper);
    cleaner.CleanUp();
}

Тем не менее, я всегда получаю следующее исключение при построении PdfCleanUpProcessor:

В экземпляре объекта не задана ссылка на объект. на iTextSharp.xtra.iTextSharp.text.pdf.pdfcleanup.PdfCleanUpProcessor.ExtractLocationsFromRedactAnnots(Int32 страницы, PdfDictionary pageDict) в iTextSharp.xtra.iTextSharp.text.pdf.pdfcleanup.PdfCleanUpProcessor.ExtractLocationsFromRedactAnnots() в iTextSharp.xtra.iTextSharp.text.pdf.pdfcleanup.PdfCleanUpProcessor..ctor(PdfStamper pdfStamper)

Похоже, что существует нулевая ссылка, созданная в extractLocationsFromRedactAnnots при назначении annotDict, поэтому следующая строка выдает исключение:

    /**
     * Extracts locations from the redact annotations contained in the document and applied to the given page.
     */
    private IList<PdfCleanUpLocation> ExtractLocationsFromRedactAnnots(int page, PdfDictionary pageDict) {
        List<PdfCleanUpLocation> locations = new List<PdfCleanUpLocation>();

        if (pageDict.Contains(PdfName.ANNOTS)) {
            PdfArray annotsArray = pageDict.GetAsArray(PdfName.ANNOTS);

            for (int i = 0; i < annotsArray.Size; ++i) {
                PdfIndirectReference annotIndirRef = annotsArray.GetAsIndirectObject(i);
                PdfDictionary annotDict = annotsArray.GetAsDict(i);
                PdfName annotSubtype = annotDict.GetAsName(PdfName.SUBTYPE);

                if (annotSubtype.Equals(PdfName.REDACT)) {
                    SaveRedactAnnotIndirRef(page, annotIndirRef.ToString());
                    locations.AddRange(ExtractLocationsFromRedactAnnot(page, i, annotDict));
                }
            }
        }

        return locations;
    }

Есть идеи, почему это происходит? Пример PDF здесь.

1 ответ

Решение

Здесь работают две проблемы: одна в коде OP, а другая в iText(Sharp).

Проблема в коде ОП

Нужно понимать, что архитектура PdfReader / PdfStamper пара - это не документ в памяти, которым манипулируют только для сохранения в конце. Вместо этого манипуляции штампом обычно записываются в выходной поток как можно скорее и не обязательно видны для другого кода, работающего на штампе.

Смысл в том, что архитектура iText (как бы дико это ни выглядело в версиях до 7.x) построена так, чтобы разрешать операции с небольшим объемом ресурсов. В серверных приложениях, которые могут обрабатывать множество PDF-файлов параллельно, это очень важно.

В данном случае код OP сначала добавляет аннотации Redact и в то же время пытается выполнить очистку, используя эти аннотации. Это не работает. Вместо этого OP либо должен добавить аннотации за один проход и применить очистку за второй, т.е.

using (PdfReader pdfReader = new PdfReader(source))
using (var stamper = new PdfStamper(pdfReader, new FileStream(temp, FileMode.Create)))
{
    // ... add REDACT annotations
}

using (PdfReader pdfReader = new PdfReader(temp))
using (var stamper = new PdfStamper(pdfReader, new FileStream(dest, FileMode.Create)))
{
    var cleaner = new PdfCleanUpProcessor(stamper);
    cleaner.CleanUp();
}

или вообще не использовать редактирование аннотаций: в конце концов, зачем добавлять аннотации только для немедленного их удаления. За это PdfCleanUpProcessor имеет второй конструктор, которому непосредственно передаются места очистки:

/**
 * Creates a {@link com.itextpdf.text.pdf.pdfcleanup.PdfCleanUpProcessor} object based on the
 * given {@link java.util.List} of {@link com.itextpdf.text.pdf.pdfcleanup.PdfCleanUpLocation}s
 * representing regions to be erased from the document.
 *
 * @param pdfCleanUpLocations list of locations to be cleaned up {@see PdfCleanUpLocation}
 * @param pdfStamper          A{@link com.itextpdf.text.pdf.PdfStamper} object representing the document which redaction
 *                            applies to.
 */
public PdfCleanUpProcessor(IList<PdfCleanUpLocation> pdfCleanUpLocations, PdfStamper pdfStamper)

Проблема в iText (Sharp)

PdfCleanUpProcessor имеет членский словарь clippingRects к которым области аннотации Redact добавляются по их индексу в массиве Annots страницы:

private IList<PdfCleanUpLocation> ExtractLocationsFromRedactAnnot(int page, int annotIndex, PdfDictionary annotDict) {
    ...
    clippingRects.Add(annotIndex, markedRectangles); 
    ...
}

Если документ на нескольких страницах имеет аннотации Redact с одинаковым индексом в соответствующем массиве Annots страницы, следовательно, этот метод при разных вызовах пытается добавить несколько элементов в член clippingRects используя тот же ключ. Сеть Dictionary класс не допускает этого и выдает исключение.

Таким образом, редактирование iTextSharp с помощью аннотаций Redact корректно работает только для документа с аннотациями Redact, только если аннотирована только одна страница!

Первоначальная разработка этой функции происходит на Java и на Java. clippingRects это HashMap что позволяет перезаписывать записи, поэтому здесь не выдается никаких исключений. Кроме того, как содержание clippingRects используются только в особом случае (использование RO или OverlayText в записях Redact), неправильные записи часто не причиняют вреда и, следовательно, могут еще не воспроизводиться.

Другие вопросы по тегам