Найти в Дзене
Dmitry Sieg | Ваш IT-гуру

Создание PDF-файлов в блокноте, часть 2. Текст.

Введение Продолжаю эксперимент с написанием PDF-файлов вручную. В этот раз я покажу, как добавить текст в наш документ. Как и в предыдущий раз, проведем исследование, используя спецификацию PDF от Adobe, найденную нами в предыдущей статье. Документ расположен по этой ссылке. Пробежимся по оглавлению и обратим внимание на подзаголовок "Text". Пролистав чуть вниз, сразу находим пример использования: Заметив загадочные похожие слова "Tf, Td, Tj", начинаю искать подробности о них, и вижу упоминание "Tf operator". Похоже, что для задания текста в документе используется конструкция вида BT...ET, а внутри набор операторов с параметрами. Но что же это за тип объекта, который не похож на словарь? Потоки Проведя поиск по спеке по конструкциям "Tf" и "BT", я обнаруживаю следующий пример (Example 3 из раздела 8.7.3.3): Итак, конструкция, задающая текст, должна находится в объекте типа stream, он же поток. В спецификации можно найти раздел 7.8, посвященный этому типу данных. Там же находим подтверж
Оглавление

Введение

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

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

Заметив загадочные похожие слова "Tf, Td, Tj", начинаю искать подробности о них, и вижу упоминание "Tf operator". Похоже, что для задания текста в документе используется конструкция вида BT...ET, а внутри набор операторов с параметрами. Но что же это за тип объекта, который не похож на словарь?

Потоки

Проведя поиск по спеке по конструкциям "Tf" и "BT", я обнаруживаю следующий пример (Example 3 из раздела 8.7.3.3):

-2

Итак, конструкция, задающая текст, должна находится в объекте типа stream, он же поток. В спецификации можно найти раздел 7.8, посвященный этому типу данных.

-3

Там же находим подтверждение тому, что текст должен находится в потоке.

-4

Осталось понять, как связать нашу пустую страницу, объект ее содержимого, шрифт текста и сам текст.

Шрифты

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

Значение поля оставим неизменным:

-5

А в пустой словарь "5 0" добавим ссылку на объект со шрифтом (я скопировал код из Example 2 части 9.2.2 "Basics of Showing Text"):

-6

Вслед за ним, я добавил сам объект шрифта:

-7

Там же, чуть выше Example 2, находим краткое описание данного словаря:

-8

Здесь дается намек на то, что шрифт может быть включен в тело самого документа, и что в нашем случае имя шрифта указывает на имеющийся в системе шрифт "Helvetica".

Объект с текстом

Итак, настал момент добавления самого текста. Я скомбинировал примеры из 8.7.3.3 и 9.2.2 и на всякий случай добавил словарь с полем /Length, отмерив длину стрима от BT до ET включительно уже знакомым приемом с индикатором Sel в блокноте. Вот, что у меня получилось:

-9

Оператор Tf в этом потоке задает используемый шрифт, Td — смещение относительно начала страницы, и Tj — сам текст.

Больше новых объектов нам не потребуется. Но необходимо скорректировать некоторые поля в PDF, т.к. мы изменили его длину и следовательно смещение xref. Изменения, которые я сделал в конце файла:

-10

Открываем полученный документ, но в нужном месте текста не видно... он оказался внизу документа!

-11

Однако, замечаем, что отступы от нижней и от левой части равны. Можно догадаться, что система координат в PDF начинается по умолчанию от нижнего левого края страницы, и оси координат имеют направления "вправо-вверх" как в классической геометрии. Под примером 2 из 9.2.2 есть такое замечание:

-12

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

Заглянем в оглавление, и от него в раздел 8.3 "Coordinate Systems". Упоминалась система координат "user system", находим соответствующее ей описание:

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

-13

Это совсем не то, что я ожидал увидеть. Поискав в интернете по ключевым словам "pdf coordinate system axis", я нашел вопрос на stackoverflow, посвященный данной проблеме. Там разработчик также задается вопросом: как сменить точку отсчета и направление осей в стандартной системе координат PDF. В одном из ответов упоминается оператор потока cm, служащий для смены матрицы трансформации (transformation matrix) на текущей странице.

Справку по этому оператору я нашел в спецификации в разделе 8.4.4 "Graphics State Operators", где также дается ссылка на описание всей математической части 8.3.2 "Coordinate Spaces" и 8.3.4 "Transformation Matrices". Не особо мудрствуя, я добавил в наш поток матрицу по аналогии с ответом на stackoverflow:

-14

Где -1 означает смену направления оси Y, а 841.89 — смещение точки отсчета на одну высоту страницы вверх (относительно старого направления).

Скорректировав смещение xref, получаем следующую картину:

-15

Это уже намного ближе к нашей цели! Осталось скорректировать направление текста. Там же, на stackoverflow, упоминалось о необходимости смены матрицы направления текста (text transformation matrix). Зададим эту матрицу с фактором scalingY = -1, и смещением по оси Y, равное высоте шрифта. Оператор для задания текстовой матрицы называется Tm.

-16

К сожалению, мне не удалось задать матрицы преобразований так, чтобы в операторе Td ось Y была повернута вниз. Но хотя бы точку отсчета мы сдвинули, и можем пользоваться привычной системой координат, только изменяя знак по оси Y. Теперь надпись находится в нужном месте и выглядит корректно, при этом по заданным координатам находится верхний левый угол текста, аналогично разметке HTML:

-17

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

В следующей статье я расскажу больше о верстке и, возможно, о включении графических объектов в наш документ.

P.S. актуальную версию документа с историей версий я выложил на Github по ссылке, откуда его можно скачать и поэкспериментировать.