У меня есть код, который извлекает текст из 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 сейчас, и видел ли кто-нибудь подобные вещи?
Кодировка строкового аргумента Tj операция полностью зависит от используемого шрифта PDF (F1 в данном случае):
Строковый операнд оператора отображения текста должен интерпретироваться как последовательность кодов символов, идентифицирующих глифы, которые должны быть нарисованы.
При использовании простого шрифта каждый байт строки должен обрабатываться как отдельный символьный код. Затем необходимо найти код символа в кодировке шрифта, чтобы выбрать глиф, как описано в 9.6.6, «Кодировка символов».
С составным шрифтом (PDF 1.2), многобайтовые коды могут использоваться для выбора глифов. В этом случае один или несколько последовательных байтов строки должны обрабатываться как односимвольный код. Длина кода и отображения из кодов в глифы определяются в структуре данных, называемой CMap, описанной в 9.7, «Составные шрифты».
(раздел 9.4.3 «Операторы отображения текста» в ISO 32000-1)
Код ОП, кажется, предполагает стандартную кодировку, такую как MacRomanEncoding или же WinAnsiEncoding, но это просто особые случаи. Как указано в приведенной выше цитате, кодирование может также представлять собой специальное смешанное многобайтовое кодирование.
Спецификация PDF в следующем разделе описывает, как правильно извлечь текст:
Соответствующий читатель может использовать эти методы в заданном приоритете для сопоставления кода символа со значением Юникода. В частности, документы PDF с тегами должны предоставлять по крайней мере один из этих методов (см. 14.8.2.4.2, «Отображение Unicode в PDF с тегами»):
Если словарь шрифтов содержит ToUnicode CMap (см. 9.10.3, «ToUnicode CMaps»), используйте этот CMap для преобразования кода символа в Unicode.
Если шрифт является простым шрифтом, который использует одну из предопределенных кодировок MacRomanEncoding, MacExpertEncoding, или же WinAnsiEncoding, или он имеет кодировку, в массив которой Различия входят только имена символов, взятые из стандартного набора символов латинского алфавита Adobe, и набор именованных символов шрифтом Symbol (см. Приложение D):
а) Сопоставить код символа с именем символа в соответствии с таблицей D.1 и шрифтами Различия массив.
б) Посмотрите имя персонажа в Adobe Glyph List (см. Библиографию), чтобы получить соответствующее значение Unicode.
Если шрифт является составным шрифтом, который использует один из предопределенных CMaps, перечисленных в Таблице 118 (кроме Identity-H и Identity-V) или чей потомок CIDFont использует Adobe-GB1, Adobe-CNS1, Adobe-Japan1 или Adobe-Korea1 коллекция персонажей:
а) Сопоставить код символа с идентификатором символа (CID) в соответствии с CMap шрифта.
б) Получить реестр и порядок набора символов, используемых шрифтом CMap (например, Adobe и Japan1) из его CIDSystemInfo толковый словарь.
c) Создайте второе имя CMap, объединив реестр и упорядочив, полученные на шаге (b) в формате Registry-ordering-UCS2 (например, Adobe-Japan1-UCS2).
d) Получите CMap с именем, созданным на шаге (c) (доступно на веб-сайте ASN; см. Библиографию).
e) Сопоставьте CID, полученный на этапе (a), с CMap, полученным на этапе (d), с получением значения Unicode.
Если этим методам не удается получить значение Unicode, невозможно определить, что представляет собой символьный код, и в этом случае соответствующий читатель может выбрать код символа по своему выбору.
(раздел 9.10.2 «Отображение кодов символов в значения Unicode» в ISO 32000-1)
Таким образом:
Просто интересно, какой тип символов в кодовом кольце появился в PDF сейчас, и видел ли кто-нибудь подобные вещи?
Да, в PDF-файлах довольно часто встречаются строковые аргументы оператора рисования текста в кодировке, совершенно отличной от чего-то ASCII. И, как намекает последний абзац во второй приведенной выше цитате, есть ситуация, когда вообще не разрешается извлечение текста (то есть без OCR), хотя есть дополнительные места, в которых можно найти отображение в Unicode.
То, что вы хотите декодировать загадочную строку в наиболее общем случае, это поле / 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|
]
Здесь вы можете увидеть / Тип кодировки / 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).