Извлечение Шрифтов Из Pdf

Сразу стоит сказать, что лучшей информации по формату, чем многомегабайтный PDFReference с сайта Adobe, нет. Для тех, кто пишет на C++, есть готовое решение — XPDF. В Linux это наиболее полнофункциональная замена продуктов Adobe. Русскоязычные материалы по этой теме поверхностны и служат только для информации, а не для практической работы.

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

Я знаю только одну такую программу, которая работает с ошибками и без исходников.

Напомню, что использование извлеченных шрифтов не всегда законно; вы можете отображать текст из документа только с помощью встроенного шрифта.

Те, кого интересовал вопрос, знают, что PDF состоит из заголовка, таблицы перекрестных ссылок (XRef), тела и трейлера (трейлера).

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

Сначала вам нужно прочитать таблицу XRef. Я рекомендую спроектировать его как класс.

Чтобы найти адрес таблицы, мы читаем файл с конца, пока не встретим тег %%EOF. Продолжайте читать назад до тега startxref. Теперь вы можете посчитать число, которое следует за этим тегом.

Вот пример конца файла: startxref 173 %%EOF число 173 — это смещение от начала данных файла до начала первой таблицы XRef. Подойдя к этому моменту, мы видим что-то вроде этого: внешняя ссылка 7628 42 0000000016 00000 н 0000001195 00000 ф и т. д. На 7628 мы пока обращать внимание не будем (это имя первого объекта, куда записывается информация о количестве страниц, например, и многое другое).

А 42 — это количество записей в этой части таблицы.

Дальше все довольно просто: читаем первое слово в 10-байтовый буфер, пропускаем пробел и читаем 5-байтовый буфер, читаем отдельный символ.

И так 42 раза.

Строки, преобразованные в целые числа, имеют следующее значение — смещение от начала данных до опорного объекта, номер поколения.

Последний символ интерпретируется следующим образом: n — объект используется, f — объект не используется, но как я уже говорил, таблица XRef может иметь продолжения в файловом потоке.

Как их найти? За таблицей всегда следует тег трейлера.

При его встрече нужно искать строку /Prev — если она есть, то дальше идет смещение к следующей таблице.

/Предыдущая 4025745 Таким образом мы читаем все таблицы, если их больше одной.

Вы можете закончить чтение, если в следующем трейлере отсутствует ключ /Prev. Признаком последней таблицы может быть и то, что она начинается с записи 0000000000 65535 f. Надо сказать, что мы читаем таблицы задом наперед, последней при чтении является первая, которая появилась при создании самого документа, а первая при чтении появилась после последнего редактирования.

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

Правда, есть и прямые объекты, адреса которых не включены в XRef, но об этом позже.

Теперь мы можем перебирать объекты документа, проверяя их тип и поступая с ними по своему усмотрению.

Объект начинается так: 7626 0 объектов содержимое объекта эндобж 7626 — это номер (имя) объекта, а 0 — номер поколения, который должен соответствовать аналогичному значению в таблице ссылок для этого объекта.

Я так понимаю, если объект меняется или редактируется, то номер генерации увеличивается.

Мы собираемся искать шрифты, для этого нам нужно прочитать словарь объекта, который представляет собой лексему, заключенную в <<… > > теги.

Если элементы словаря имеют такую структуру, например: /FirstChar 32 где слово после косой черты — это ключ, а необязательное значение после пробела — это значение.

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

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

Указанное значение также может включать в себя вложенные или невложенные элементы следующих типов: (… ) — текстовые строки <… > - шестигранные строки [.

] ] - массивы Строка значений продолжается до следующей косой черты или перевода строки.

Чтобы идентифицировать объект шрифта, вам нужно найти комбинацию в словаре: /Тип /Шрифт Теперь мы фильтруем шрифты Truetype по содержимому в словаре последовательности: /Подтип /TrueType Мы игнорируем остальные ключи, потому что хотим просто извлечь шрифты.

Но самого шрифта в этом объекте мы, скорее всего, не найдём.

Просто набор ключей, которые нам не нужны.

Читаем одно из них: /FontDescriptor 1675 0 р Если такой ключ отсутствует, значит шрифт внешний и не встроен в документ. Далее идет номер поколения этого объекта, а символ R указывает на то, что это ссылка.

Таблицу XRef мы уже прочитали и теперь можем перейти к данным шрифта, выполнив поиск смещения объекта с номером 1675. Однако возможен и такой вариант: /Фонтдескриптор << dictionary and/or font data > > Будем считать, что мы перешли по ссылке на прямой объект. Его словарь должен содержать следующие ключи: /Тип /Фонтдескриптор Этот объект также содержит много полезной информации о шрифте, но самого шрифта опять же нет. Это не моя вина — все претензии к Adobe. Нам нужен такой ключ /FontFile2 1676 0 Ч Знакомый дизайн.

Перейдем к следующему объекту.

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

endstream. Здесь надо сказать, что наличие двоичных данных не позволяет использовать готовые парсеры текста.

Я многое перепробовал, и мне пришлось написать свою собственную с нуля.

Двоичные данные можно прочитать сразу, поскольку словарь потока имеет ключ /Length с длиной потока.

Если вы попытаетесь сохранить извлеченный поток в файл с расширением TTF, система объявит, что это не шрифт. Все правильно, нам нужно его разжать.

Шрифт чаще всего сжимается с помощью zip, но для уверенности вы можете проверить это по наличию ключа /FlateDecode. Если мы работаем в Delphi, то используем стандартный ZLib. Мы можем получить размер буфера для распакованных данных из словаря потока, используя ключ /Length1. Ну и нужно знать, что встроенный в документ шрифт содержит только те глифы, которые используются в документе.

Я думаю, что после этих набросков можно взять в одну руку шестнадцатеричную программу просмотра, в другую PDFReference и купить себе Acrobat Reader. Теги: #pdf #delphi. шрифты #truetype #программирование

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.