Как найти словарь трейлера? - PullRequest
5 голосов
/ 24 июня 2011

Проходя через спецификацию PDF, он говорит, что trailer предшествует startxref. Что для меня говорит, что xref может появиться в любом месте документа, но trailer все еще появляется перед startxref. Это имеет смысл до тех пор, пока вам не придется анализировать его, потому что вы должны анализировать в обратном порядке, вы не можете принимать во внимание комментарии или строки. Давай тогда станем немного более странными.

trailer<< %\
  /Size 4 %\
  /Root 1 0 R %\
  /Info 4 0 R %\
  /Key (\
trailer<< %\
  /Size 4 %\
  /Root 2 0 R %\
  /Info 3 0 R %\
>>%)
>>&)
% test test )
startxref
 15
%%EOF

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

Обновление - кажется, этот трейлер открывается в Acrobat Reader

%PDF-1.3
%âãÏÓ
xref
0 4
00000000 65535 f
00000110 00000 n
00000250 00000 n
00000315 00000 n
00000576 00000 n

1 0 obj <<
  /Type /Catalog
  /Pages 2 0 R
  /OpenAction [ 3 0 R /XYZ null null null ]
  /PageLabels << /Nums [0 << /S /D >> ] >>
>>
endobj
2 0 obj <<
  /Type /Pages
  /Kids [ 3 0 R ]
  /Count 1
>>
endobj
3 0 obj <<
  /Type /Page
  /Parent 2 0 R
  /Resources << >>
  /MediaBox [ 0 0 612 792 ]
>>
endobj
4 0 obj <<
  /Producer (Me)
  /CreationDate (D:20110626000000Z)
>>
endobj

trailer<< %\
  /Size 4 %\
  /Root 1 0 R %\
  /Info 4 0 R %\
  /Key (\
trailer<< %\
  /Size 4 %\
  /Root 2 0 R %\
  /Info 3 0 R %\
>>%)
>>%)
% test test )
startxref
 15
%%EOF

Что касается синтаксиса, это соответствует спецификации. Каким-то образом они, кажется, могут знать, есть ли они в комментарии или в строке. Разбирая L-R, второй трейлер находится в строке с хвостом%, с комментарием после трейлера. Но разбор R-L, вы понятия не имеете, является ли первый) частью комментария или концом определения строки.

Другой пример:

%PDF-1.3
%âãÏÓ
xref
0 8
0000000000 65535 f
0000000210 00000 n
0000000357 00000 n
0000000428 00000 n
0000000533 00000 n
0000000612 00000 n
0000000759 00000 n
0000000830 00000 n
0000000935 00000 n

1 0 obj <<
  /Type /Catalog
  /Pages 2 0 R
  /OpenAction [ 3 0 R /XYZ null null null ]
  /PageLabels << /Nums [0 << /S /D >> ] >>
>>
endobj
2 0 obj <<
  /Type /Pages
  /Kids [ 3 0 R ]
  /Count 1
>>
endobj
3 0 obj <<
  /Type /Page
  /Parent 2 0 R
  /Resources << >>
  /MediaBox [ 0 0 612 792 ]
>>
endobj
4 0 obj <<
  /Producer (Me)
  /CreationDate (D:20110626000000Z)
>>
endobj
5 0 obj <<
  /Type /Catalog
  /Pages 6 0 R
  /OpenAction [ 7 0 R /XYZ null null null ]
  /PageLabels << /Nums [0 << /S /D >> ] >>
>>
endobj
6 0 obj <<
  /Type /Pages
  /Kids [ 7 0 R ]
  /Count 1
>>
endobj
7 0 obj <<
  /Type /Page
  /Parent 6 0 R
  /Resources << >>
  /MediaBox [ 0 0 100 100 ]
>>
endobj
8 0 obj <<
  /Producer (Me)
  /CreationDate (D:20110626000000Z)
>>
endobj

trailer<< %\
  /Size 8 %\
  /Root 1 0 R %\
  /Info 4 0 R %\
  /Key (\
trailer<< %\
  /Size 8 %\
  /Root 5 0 R %\
  /Info 8 0 R %\
>>%)
>>%)
% test test )
startxref
 17
%%EOF

Этот пример правильно отображается в Adobe. В моем последнем случае вы утверждали, что он потерпит неудачу, потому что «корневой» узел недействителен, но этот новый пример, корень действителен, но фактически никогда не используется. Так не должен ли он отображать окно 100x100 вместо 8,5 "x11"?

В отношении ресурсов

  (Required; inheritable) A dictionary containing any resources required by the page 
(see Section 3.7.2, “Resource Dictionaries”). If the page requires no resources, the 
value of this entry should be an empty dictionary. Omitting the entry entirely
indicates that the resources are to be inherited from an ancestor node in the page 
tree.

Ответы [ 5 ]

4 голосов
/ 24 июня 2011

Оператор startxref обычно находится в конце файла, а перед ним стоит трейлер.

Обновление: Выше вводное предложение было сформулировано недостаточно четко, как правильно заметил Джереми Уолтон (хотя более поздние комментарии в моем ответе намекали на исключения). Он должен был прочитать: "Оператор startref обычно появляется в конце файла как один экземпляр, с трейлером перед ним (если только ваш файл не подвергался инкрементным обновлениям, в этом случае у вас могут быть разные экземпляры перекрестные ссылки с различными трейлерами. "

Если есть комментарии, добавленные в PDF, они учитываются так же, как и "настоящий" код описания страницы PDF, когда дело доходит до подсчета байтов для расчета смещения в байтах таблицы внешних ссылок. Поэтому нетрудно разобрать его правильно.

Цитировать прямо "изо рта лошади" ( PDF спецификация ISO 32000-1 , раздел 7.5.5):

" трейлер файла PDF позволяет соответствующему считывателю быстро находить таблицу перекрестных ссылок и определенные специальные объекты. Соответствующие читатели должны читать файл PDF с конца. Последняя строка файла должен содержать только маркер конца файла, %%EOF. Две предыдущие строки должны содержать, по одной на строку и по порядку, ключевое слово startxref и смещение байта в декодированном потоке от начала файла до начало xref keyword в последнем разделе перекрестных ссылок. Строке startxref должен предшествовать трейлерный словарь , состоящий из ключевого слова trailer, за которым следует ряд пар ключ-значение, заключенных в двойные угловые скобки [...] "

Здесь необходимо принять во внимание ключевое выражение: "LAST раздел перекрестных ссылок" .

Если вы имеете в виду обновленные трейлеры, то посмотрите Раздел 7.5.6.

Да, вы должны разобрать в обратном порядке. Первый раздел перекрестных ссылок, который нужно прочитать, является последним в файле, и у него будет предыдущий последний трейлер. Второй, который нужно прочитать, - это последний, но один, появляющийся в файле, с предшествующим последним, но единственным трейлером. Etc.pp .... Если вам нужно прочитать более одного раздела трейлера / ссылки, каждый из прочитанных должен содержать ссылку на следующий, который нужно прочитать.

Если вы думаете, что «комментарии» - это то, что вы можете свободно вставить в PDF, не повреждая его структуру, тогда думайте иначе. После того, как вы вставили комментарии, вы должны обновить хотя бы таблицу внешних ссылок (и, возможно, /Length ключи объектов).


Обновление 2: Словарь trailer<<...>>, созданный Джереми, вероятно, вообще не является допустимым словарем, поэтому он также не является действительным трейлером словарем. ..

В любом случае, согласно спецификации, словарь трейлера должен состоять из «серии пар ключ-значение» . «Правильные» ключи в словаре трейлера ограничены довольно узким набором, некоторые из которых даже являются необязательными (см. Таблицу 15 в разделе 7.5.5).

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

trailer<<%) >>
% test test )

Что, конечно, вовсе не словарь, поскольку здесь мы не видим пары ключ-значение.

Его полный пример также недействителен, потому что «ключ» с именем /Key не входит в число допустимых имен ключей для трейлера (которые, согласно таблице 15: /Size, /Prev, /Root, /Encrypt, /Info, /ID, /XRefStm).

Таким образом, Джереми должен сделать в своем коде парсинга PDF то же самое, что делают все здравомыслящие и даже самые безумные библиотеки обработки PDF: отказаться от явно недопустимых конструкций вместо поиска смысла в них и сказать пользователю, что "ваш проклятый PDF поврежден, потому что мы не можем определить действительные ключи в предполагаемом разделе трейлера файла ".

3 голосов
/ 28 июня 2011

Q: Док, мне больно, когда я делаю это.
A: Не делайте этого.

Правильный способ анализа конца PDF-файла выглядит примерно так:

  1. Найти последний startxref
  2. Создать резервную копию этого байтового смещения и начать анализ записей таблицы внешних ссылок
  3. После последней таблицы внешних ссылок, разбор трейлера.

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

Так почему же вы просто хотите трейлер?


Когда я просматривал PDF Reference, я ожидал найти строку текста, в которой указывалось, что заголовок / body / xref / trailer должен быть в таком порядке. Я не сделал.

То, что я ДЕЙСТВИТЕЛЬНО нашел, было это:

Основной соответствующий PDF-файл должен состоять из следующих четырех элементов (см. Рисунок 2):
- однострочный заголовок ...
- тело ...
- Таблица перекрестных ссылок ...
- трейлер ...

Перед этими разделами есть маркеры, а не цифры.

Так что все намеки на то, что соответствующий PDF может сойти с рук, поменяв порядок тела и внешней ссылки. С другой стороны, заголовок требуется , чтобы быть первым, трейлер требуется , чтобы быть последним, и все разделы PDF перечислены в этом порядке. Это подразумевает порядок, но не выдержит судебного разбирательства.

Но если вы посмотрите на рисунок 2 (из главы 7, раздел 5.1), озаглавленный «Начальная структура файла PDF», вы увидите порядок, определенный визуально. Это немного тонко, но я все равно буду за нее цепляться.

Я не удивлюсь, обнаружив, что PDF, который поместил свое тело после таблицы внешних ссылок, сломал некоторые программы просмотра PDF (особенно искаженный PDF, где программа пыталась это исправить).

Я работаю с файлами PDF уже более десяти лет. За все это время я никогда не видел PDF-файл, где внешняя ссылка была перед телом. И я видел некоторые действительно испорченные PDF-файлы.

Так что, хотя мой «правильный способ анализа PDF» может не быть Iron Clad, он все же довольно долговечен.


И если вы абсолютно настаиваете на резервном копировании, чтобы найти ключевое слово «трейлер», то вы можете искать токены «закрыть массив или словарь» после того, как проанализируете найденный трейлер. Если бы он был заключен в строку, все слэши имен должны были бы быть экранированы, что привело бы к плохому синтаксическому анализу. Вы не можете иметь пробелы в имени ... так что остаются только массив и словарь.

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

2 голосов
/ 01 июля 2011

Я думаю, что нашел решение.После обширного тестирования и других вещей с Adobe, я обнаружил, что Adobe делает, это найти последнюю известную конструкцию, которая может быть проанализирована, и работать оттуда, вперед.Затем он находит последний трейлер, который можно правильно проанализировать.Таким образом, даже если существует правильный корневой узел, который в трейлере перед последним действительным трейлером, который может быть проанализирован, если корень в последнем трейлере недействителен, он все равно не будет работать.Также было бы хорошо отметить, что это все еще разбор, основанный на токене.как трейлеры между () игнорируются, так и трейлеры между потоком / конечным потоком, если этот поток не имеет недопустимой длины или длины, указанной в объекте после потока (так как эти объекты не указаны в таблице внешних ссылок).Теперь Adobe, похоже, пошла на этот дополнительный шаг, фактически найдя трейлеры в «пробелах» и в таблице внешних ссылок, это не соответствует текущей модели спецификации, так как трейлер находится в конце, а не в теле.или таблица внешних ссылок.Поэтому я считаю, что лучшей моделью является получение наибольшего смещения таблицы xref и расположения таблицы xref, если таблица xref находится после наибольшего смещения объекта, затем используйте это и продолжайте работу оттуда.Это позволит мне правильно анализировать строки и комментарии, не беспокоясь.Спасибо всем за помощь в этом вопросе.Надеюсь, это поможет людям создать более надежный анализатор PDF.

2 голосов
/ 27 июня 2011

Джереми неоднократно редактировал свой код вопроса и примера.Это сделало мой первоначальный ответ и некоторые из моих первоначальных комментариев частично недействительными и упускало суть.

Факт (и хорошо известный среди людей в допечатной торговле и промышленности): Adobe делает в довольно многих случаяхмолча и без предупреждения и отображения файлов PDF, которые не проходят строгую проверку достоверности.

Джереми, похоже, создал такой случай.Его последний пример заставил бы любой парсер PDF интерпретировать следующий фрагмент как трейлер (я обрезал комментарии):

trailer<<
  /Size 4
  /Root 2 0 R
  /Info 3 0 R
>>

Однако получение информации в этом трейлере приведет к тому, что парсер будет искать /Rootв объекте 2 (в то время как объект 2 фактически имеет значение /Type /Pages, когда он должен быть /Type /Catalog для того, чтобы быть корневым объектом).

Как следствие, интерпретатор PDF должен будет

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

Adobe, похоже, следует альтернативе (a).

Ghostscript, кажется, следует альтернативе(б).


Обратите внимание, , что согласно моему подсчету байтов, у примера Джереми в PDF есть еще одна проблема: его таблица внешних ссылок недопустима.Он содержит только 16 байтов на строку вместо 20. Из документа спецификации PDF:

[....] сами записи перекрестных ссылок, по одному на строку.Каждая запись должна быть ровно 20 байтов, включая маркер конца строки.Существует два вида записей перекрестных ссылок: один для объектов, которые используются, а другой для объектов, которые были удалены и, следовательно, являются бесплатными.Оба типа записей имеют одинаковые основные форматы, различаемые по ключевому слову n (для используемой записи) или f (для бесплатной записи).Формат используемой записи должен быть:

nnnnnnnnnn ggggg n eol

где:

nnnnnnnnnn должно быть 10-значным байтовым смещением в декодированном потоке
ggggg должно быть 5-значным номером генерации
n должно быть ключевым словом, идентифицирующим это как используемую запись
eol должно быть двухсимвольной последовательностью конца строки

Смещение байта в декодированном потоке должно представлять собой 10-значное число, дополненное, если необходимо, начальными нулями, дающее число байтов от начала файла до начала объекта.

Таким образом, чтобы сделать таблицу внешних ссылок Джереми верной, к ней следует добавить еще 2 ведущих «0» и прочитать:

xref
0 4
0000000000 65535 f 
0000000110 00000 n 
0000000250 00000 n 
0000000315 00000 n 
0000000576 00000 n 

Однако добавление этих 2 '0' к каждой строке внешней ссылки также смещает каждуюобъект на 10 байт, поэтому цифры nnnnnnnnnn также должны быть исправлены (ленивый, я этого не делал).

Итак, Acrobat открылсозданный файл Джереми (без какого-либо предупреждения)

  • (1), несмотря на недопустимое определение трейлера, и
  • (2), несмотря на явно несовместимую таблицу внешних ссылок.

Это добавляет еще два доказательства к тому, что я изложил во втором абзаце: при разборе PDF в Adobe принимаются файлы, которые нарушают собственный стандарт PDF Adobe.

Это прискорбно.Это позволяет ленивым разработчикам писать небрежный код, который генерирует несовместимые файлы PDF без наказания.Тот факт, что Adobe не отвергает такие дрянные файлы, может быть в интересах «удобства для пользователя», но способствует нарушениям стандарта.По крайней мере, Adobe всегда должен выдавать предупреждения при встрече с такими вещами.

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

В любом случае: я видел много несовместимых файлов PDF, испускаемых дрянными генераторами PDF. Но до сих пор я никогда не сталкивался с тем, в котором комментарии были добавлены в раздел trailer. Поэтому попытка охватить угловые случаи, возможно, должна начинаться с более низко висящих фруктов, чем эта.

1 голос
/ 24 июня 2011

Словарь трейлера следует за разделом внешних ссылок.Основываясь на значении startxref, вы переходите к началу раздела xref.После прочтения раздела внешних ссылок вы попадете в словарь трейлера.Ключевое слово трейлера всегда является первым в своей строке (перед ним разрешены пробелы).PDF-файлы допускают инкрементные обновления, поэтому вы можете встретить PDF-файлы с несколькими разделами внешних ссылок и трейлерами, но правило обработки такое же, сначала обработайте раздел внешних ссылок, а затем трейлер.Если файл содержит инкрементные обновления, раздел трейлера будет содержать ссылку на предыдущий раздел внешних ссылок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...