PHP-фильтр FlateDecode PDF-поток, возвращающий символы смещения

У меня есть код, который извлекает текст из PDF-файла с помощью класса filetotext. Работал до прошлой недели, когда что-то изменилось в генерируемом pdf. Странно то, что кажется, что символы есть и правильные, как только я добавляю 29 к порядку символа.

Пример отладочной распечатки ответа:

/F1 7.31 Tf
0 0 0 rg
1 0 0 1 195.16 597.4 Tm
($PRXQW)Tj
ET
BT

Код использует gzuncompress в разделе потока pdf. $PRXQW - это сумма, и добавление 29dec к порядку каждого символа дает мне это. Но иногда символ не будет таким точным переводом, например, каким должен быть ) в тексте появляется два байта 5C66.

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


person Tim    schedule 13.08.2015    source источник
comment
Некоторое подмножество или пользовательская кодировка были введены со шрифтом, на который ссылается / F1. Определение этого шрифта в PDF может пролить некоторый свет, или посмотрите на настройки шрифта при создании PDF.   -  person dwarring    schedule 14.08.2015
comment
@dwarring, спасибо, это помогло мне двигаться вперед.   -  person Tim    schedule 15.08.2015


Ответы (2)


Кодировка строкового аргумента операции Tj полностью зависит от используемого шрифта PDF (в данном случае F1):

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

В простом шрифте каждый байт строки должен рассматриваться как отдельный код символа. Затем следует искать код символа в кодировке шрифта для выбора глифа, как описано в 9.6.6, «Кодировка символов».

В составном шрифте (PDF 1.2) для выбора глифов можно использовать многобайтовые коды. В этом случае один или несколько последовательных байтов строки должны рассматриваться как код одного символа. Длина кода и отображение кодов в глифы определяются в структуре данных, называемой CMap, описанной в 9.7, «Композитные шрифты».

(раздел 9.4.3 «Операторы отображения текста» в ИСО 32000-1)

Код OP, кажется, предполагает стандартную кодировку, такую ​​как MacRomanEncoding или WinAnsiEncoding, но это всего лишь частные случаи. Как указано в приведенной выше цитате, кодировка также может быть некоторой специальной смешанной многобайтовой кодировкой.

Спецификация PDF в следующем разделе описывает, как правильно извлекать текст:

Соответствующий читатель может использовать эти методы с заданным приоритетом для сопоставления кода символа со значением Unicode. Документы PDF с тегами, в частности, должны обеспечивать по крайней мере один из этих методов (см. 14.8.2.4.2, «Отображение Unicode в PDF с тегами»):

  • Если словарь шрифтов содержит CMap ToUnicode (см. 9.10.3, «ToUnicode CMap»), используйте этот CMap для преобразования кода символов в Unicode.

  • Если шрифт представляет собой простой шрифт, который использует одну из предопределенных кодировок MacRomanEncoding, MacExpertEncoding или WinAnsiEncoding или имеет кодировку, чей массив Differences включает только имена символов, взятые из стандартного набора латинских символов Adobe, и набор именованных символов шрифта Symbol (см. Приложение D):

    a) Сопоставьте код символа с именем символа в соответствии с Таблицей D.1 и массивом Различий шрифта.

    б) Найдите имя символа в Списке глифов Adobe (см. библиографию), чтобы получить соответствующее значение Unicode.

  • Если шрифт представляет собой составной шрифт, который использует один из предопределенных CMap, перечисленных в таблице 118 (кроме Identity-H и Identity-V), или чей потомок CIDFont использует Adobe-GB1, Adobe-CNS1, Adobe-Japan1 или Adobe-Korea1 коллекция персонажей:

    a) Сопоставьте код символа с идентификатором символа (CID) в соответствии с CMap шрифта.

    b) Получите реестр и порядок набора символов, используемого CMap шрифта (например, Adobe и Japan1), из его словаря CIDSystemInfo.

    c) Создайте второе имя CMap, объединив реестр и порядок, полученные на шаге (b), в формате реестр–порядок–UCS2 (например, Adobe–Japan1–UCS2).

    d) Получите CMap с именем, созданным на шаге (c) (доступно на веб-сайте ASN; см. библиографию).

    e) Сопоставьте CID, полученный на этапе (a), в соответствии с CMap, полученным на этапе (d), с получением значения Unicode.

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

(раздел 9.10.2 «Сопоставление кодов символов со значениями Unicode» в ИСО 32000-1)

Таким образом:

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

Да, в PDF-файлах из дикой природы довольно часто используются строковые аргументы оператора рисования текста в кодировке, полностью отличной от чего-то ASCII-ish. И, как намекает последний абзац во второй цитате выше, есть ситуации, когда извлечение текста вообще невозможно (то есть без OCR), хотя есть дополнительные места, где можно искать сопоставление с Unicode.

person mkl    schedule 14.08.2015
comment
Спасибо за подробное объяснение. После комментария к моему исходному сообщению я получил исходный документ, который работал, и через свойства документа обнаружил, что кодировка в разделе «Шрифт» показала различия. Рабочий файл был «Ansi», а новый файл теперь имеет «Identity-H». - person Tim; 14.08.2015
comment
Я буду искать правильное отображение или в конечном итоге использую то, что я расшифровал до сих пор. Похоже, что новые файлы теперь используют два байта на символ, скорее всего, из этой кодировки «Identity-H». - person Tim; 14.08.2015
comment
Верно, Identity-H — это двухбайтовая кодировка. Он сопоставляет 2-байтовые коды символов в диапазоне от 0 до 65 535 с тем же 2-байтовым значением CID, сначала интерпретируя старший байт. - person mkl; 14.08.2015
comment
Все это указало мне правильное направление. Я нашел данные картирования в PDF. В этом конкретном случае их 5 в файле, и их объединение дает мне то, что мне нужно. Мое сопоставление с десятичным числом плюс 29 подтверждается этими таблицами сопоставления :). - person Tim; 15.08.2015

То, что вы пытаетесь декодировать загадочную строку в самом общем случае, - это поле /Encoding выбранного шрифта, в вашем случае шрифт /F1. Более чем вероятно, что используется схема кодирования /Identity-H, которая может содержать произвольное сопоставление 16-битных символов в строках PDF с символами UTF-16.

Вот пример из парсера PDF, который я пишу. Каждая страница содержит словарь ресурсов, который содержит словарь шрифтов:

[&3|0] => Array [
   [/Type] => |/Page|
   [/Resources] => Array [
      [/Font] => Array [
         [/F1] => |&5|0|
         [/F2] => |&7|0|
         [/F3] => |&9|0|
         [/F4] => |&14|0|
         [/F5] => |&16|0|
      ]
   ]
   [/Contents] => |&4|0|
]

В моем случае /F3 создавал непригодный для использования текст, поэтому просмотр /F3:

[&9|0] => Array [
    [/Type] => |/Font|
    [/Subtype] => |/Type0|
    [/BaseFont] => |/Arial|
    [/Encoding] => |/Identity-H|
    [/DescendantFonts] => |&10|0|
    [/ToUnicode] => |&96|0|
]

Здесь вы можете видеть, что тип /Encoding — /Identity-H. Сопоставление декодирования символов для символов декодирования, используемых в /F3, сохраняется в потоке, на который ссылается /ToUnicode. Вот релевантный текст из потока, на который ссылается '&96|0' (96 0 R). Остальное опущено как шаблон и может быть проигнорировано:

...
beginbfchar
<0003> <0020>
<000F> <002C>
<0015> <0032>
<001B> <0038>
<002C> <0049>
<003A> <0057>
endbfchar
...
beginbfrange
<0044> <0045> <0061>
<0047> <004C> <0064>
<004F> <0053> <006C>
<0055> <0059> <0072>
endbfrange
...
beginbfchar
<005C> <0079>
<00B1> <2013>
<00B6> <2019>
endbfchar
...

16-битные пары между beginbfchar и endbfchar представляют собой отображения отдельных символов. Например, ‹0003> (0x0003) отображается на ‹0020> (0x0020), который является символом пробела.

16-битные триплеты между beginbfrange и endbfrange являются отображениями диапазонов символов. Например, символы от ‹0055> (первый) до ‹0059> (последний) отображаются на ‹0072>, ‹0073>, ‹0074>, ‹0075> и ‹0076> (от 'r' до 'v' в UTF16 и ASCII).

person Greg Young    schedule 20.03.2016