Не удается прочитать некоторые PDF-файлы с помощью iTextSharp - PullRequest
4 голосов
/ 25 февраля 2011

У меня есть приложение Win32, которое читает PDF-файлы с использованием iTextSharp, которое вставляет изображение в документ в качестве печати. ​​

Оно отлично работает с 99% файлов, которые мы обрабатываем в течение года, но в наши дни некоторыефайлы просто не читаются.Когда я выполняю код ниже:

string inputfile = "C:\test.pdf";
PdfReader reader = new PdfReader(inputfile);

Это дает исключение:

System.NullReferenceException occurred
  Message="Object reference not set to an instance of an object."
  Source="itextsharp"
  StackTrace:
       em iTextSharp.text.pdf.PdfReader.ReadPages()
       em iTextSharp.text.pdf.PdfReader.ReadPdf()
       em iTextSharp.text.pdf.PdfReader..ctor(String filename, Byte[] ownerPassword)
       em iTextSharp.text.pdf.PdfReader..ctor(String filename)
       em MyApp.insertSeal() na C:\MyApp\Stamper.cs:linha 659

PDF-файлы, которые выдают это исключение, обычно могут быть прочитаны Adobe PDF, и когда я открываю один изэти файлы с Acrobat и сохранить его, я могу прочитать этот сохраненный файл с моим приложением.

Файлы повреждены, но все еще могут быть открыты с помощью Adobe Reader?

Я поделюсь с вами двумя образцами файлов.

Файл, который НЕ работает: Not-Ok-Version.pdf

И файл, который работает после открытия и сохранения его с помощью Acrobat.Загрузите его здесь OK-Version.pdf

Ответы [ 5 ]

9 голосов
/ 22 марта 2011

Вот (java, извините) источник для readPages:

protected internal void ReadPages() {
  catalog = trailer.GetAsDict(PdfName.ROOT);
  rootPages = catalog.GetAsDict(PdfName.PAGES);
  pageRefs = new PageRefs(this);
}

trailer, каталог , rootPages , and pageRefs` - все переменные-члены PdfReader.

Если трейлер или корневой / каталожный объект PDF-файла просто отсутствуют, ваш PDF-файл ДЕЙСТВИТЕЛЬНО ПОВРЕЖДЕН. Скорее всего, таблица внешних ссылок немного отклонена, и рассматриваемые объекты просто не совсем там, где они должны быть (что плохо, но можно восстановить).

ОДНАКО, когда PdfReader впервые открывает PDF, он анализирует ВСЕ объекты в файле и преобразует их в соответствующие классы, производные от PdfObject.

То, что он не не делает , проверяет, что номер объекта, указанный в таблице внешних ссылок, и номер объекта, считанные из файла Actual Match. Весьма маловероятно, но возможно. Плохое программное обеспечение может записывать свои объекты PDF в неправильном порядке, но сохранять правильные смещения байтов в таблице внешних ссылок. Программное обеспечение, которое перекрывает номер объекта из таблицы внешних ссылок номером из этого конкретного байтового смещения в файле, подойдет.

iText не в порядке.

Я все еще хочу посмотреть PDF.


Да. Этот PDF сломан хорошо. В частности:

Первые 70 КБ файла или около того определяют довольно чистый маленький PDF. Затем изменения были добавлены в PDF.

Проверьте это. Кто-то попытался добавить изменения в PDF и потерпел неудачу. Плохо. Чтобы понять, насколько плохо, позвольте мне объяснить некоторый внутренний синтаксис PDF, проиллюстрированный на этом примере:

%%PDF1.6
1 0 obj
<</Type/SomeObject ...>>
endobj
2 0 obj
<</Type/SomeOtherObj /Ref 1 0 R>>
endobj
3 0 obj
...
endobj
<etc>
xref
0 10
0000000000 65535 f
0000000010 00001 n
0000000049 00002 n
0000000098 00003 n
...
trailer
<</Root 4 0 R /Size 10>>
startxref 124
%%EOF

Итак, у нас есть заголовок / версия "%% PDF1.v", список объектов (здесь они называются словарями), таблица перекрестных ссылок (x), в которой перечислены смещения байтов и номера объектов всех объектов в список и трейлер с указанием корневого объекта и количества объектов в PDF, а также смещение байта в «x» в «xref».

Вы можете добавить изменения в существующий PDF. Для этого вы просто добавляете новые или измененные объекты после существующего %% EOF, таблицу перекрестных ссылок на эти новые объекты и трейлер. Трейлер добавленного изменения должен включать ключ / Prev с байтовым смещением к предыдущей таблице перекрестных ссылок.

В вашем PDF-файле NOT-OKAY кто-то пытался добавить изменения в PDF, И УЖАСНО УДАЛЕН .

Оригинальный PDF все еще там, без изменений. Это то, что Reader показывает вам, и что вы получаете, когда сохраняете PDF. Я взломал все после первого %% EOF в шестнадцатеричном редакторе, и файл был в порядке.

Итак, вот макет вашего НЕ-OKAY pdf:

%PDF1.4.1
1 0 obj...
2 through 7
xref
0 7
<healthy xref>
trailer <</Size 8 /Root 6 0 R /Info 7 0 R>>
startxref 68308
%%EOF

Пока все хорошо. Здесь все становится ужасно

<binary garbage>
endstream
endobj
xref 
0 7
<horribly wrong xref>
trailer <</ID [...] /Info 1 0 R /Root 2 0 R /Size 7>>
startxref 223022
%%EOF

Единственное, что ПРАВИЛЬНО в этом разделе - это значение startxref.

Проблемы:

  • Второй трейлер не имеет ключа / Prev.
  • ВСЕ смещения байтов во второй таблице внешних ссылок неверны.
  • Является частью объекта «поток», но начало этого объекта пропадает. Потоки должны выглядеть примерно так

1 0 obj
<</Type/SomeType/Length 123>>
stream
123 bytes of data
endstream
endobj

Конец этого файла состоит из некоторой части (сжатого, я бы себе представлял) потока ... но без словаря в начале, говорящего нам, что фильтрует его использование и как долго это (не говоря уже о какие-либо пропущенные данные), с этим ничего не поделаешь.

Я подозреваю, что кто-то пытался полностью перестроить этот PDF, а затем случайно написал оригинальные 70 КБ в начале своей версии. Kaboom.

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

Когда iText не удается открыть PDF:
1. Выполните поиск в обратном направлении в файле в поисках второй до последней %%EOF. Не обращайте внимания на тот, что в самом конце, мы хотим предыдущее состояние файла. 2. Удалите все после второго-последнего %%EOF (если есть) и попробуйте открыть его снова.

Печально то, что этот поврежденный PDF-файл мог полностью отличаться от «оригинальных» 70 КБ, а затем некоторая ошибка ввода-вывода перезаписала первую часть файла. Вряд ли, но нет уверенности.

3 голосов
/ 25 февраля 2011

Учитывая, что они теперь до версии 5.0, я предполагаю, что вы видите все большее количество PDF-файлов, записанных в спецификации версий PDF, которые ваша версия iTextSharp не поддерживает.Возможно, пришло время сделать обновление.

2 голосов
/ 19 февраля 2015

Может быть, это кому-нибудь поможет ... У меня был код, который работал годами, который начинал зависать при чтении закладок из файла PDF (переменная схемы ниже). Оказалось, что он сломался, когда код был обновлен с .NET 4.0 до .NET 4.5.
Как только я откатился до .NET 4.0, он снова заработал.

        RandomAccessFileOrArray raf = null;
        PdfReader reader1 = null;
        System.Collections.ArrayList outlines = null;
        raf = new iTextSharp.text.pdf.RandomAccessFileOrArray(sFile);
        reader1 = new iTextSharp.text.pdf.PdfReader(raf, null);
        outlines = iTextSharp.text.pdf.SimpleBookmark.GetBookmark(reader1);

Только для заметок, тот же проект веб-приложения VS использует AjaxControlToolkit (от NuGet). Прежде чем откатить его, я также обновил iTextSharp до версии 5.5.5, и он все еще висел на той же строке.

1 голос
/ 24 марта 2011

Когда я опускаю источник и запускаю его для плохого PDF, в ReadPdf() в 4-м try блоке возникает исключение, когда он вызывает ReadDocObj():

"Invalid object number. at file pointer 16"

tokens.StringValue - это j

@ Марк Сторер, ты парень из iText, так что, возможно, это что-то для тебя значит.

С более высокого уровня, по крайней мере, на мой взгляд, кажется, что когда вызывается RebuildXref() (что я предполагаю, когда читается неверный PDF), он перестраивает trailer, но не catalog. Именно на это и жалуется NRE. Опять же, это всего лишь предположение.

0 голосов
/ 08 октября 2014

Также убедитесь, что ваш html не содержит тега hr при конвертации html в pdf

hdnEditorText.Value.Replace("\"", "'").Replace("<hr />", "").Replace("<hr/>", "")
...