Работаем с PDF из Perl::Журнал СА 3.2006
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
  Статьи

Дата-центры  

Дата-центры: есть ли опасность утечки данных?

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

 Читать далее...

Книжная полка  

Защиты много не бывает

Среди книжных новинок издательства «БХВ» есть несколько изданий, посвященных методам социальной инженерии

 Читать далее...

Событие  

В банке рассола ждет сисадмина с полей фрактал-кукумбер

Читайте впечатления о слете ДСА 2024, рассказанные волонтером и участником слета

 Читать далее...

Организация бесперебойной работы  

Бесперебойная работа ИТ-инфраструктуры в режиме 24/7 Как обеспечить ее в нынешних условиях?

Год назад ИТ-компания «Крок» провела исследование «Ключевые тренды сервисного рынка 2023». Результаты

 Читать далее...

Книжная полка  

Читайте и познавайте мир технологий!

Издательство «БХВ» продолжает радовать выпуском интересных и полезных, к тому же прекрасно

 Читать далее...

СУБД PostgreSQL  

СУБД Postgres Pro

Сертификация по новым требованиям ФСТЭК и роль администратора без доступа к данным

 Читать далее...

Критическая инфраструктура  

КИИ для оператора связи. Готовы ли компании к повышению уровня кибербезопасности?

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

 Читать далее...

Архитектура ПО  

Архитектурные метрики. Качество архитектуры и способность системы к эволюционированию

Обычно соответствие программного продукта требованиям мы проверяем через скоуп вполне себе понятных

 Читать далее...

Как хорошо вы это знаете  

Что вам известно о разработках компании ARinteg?

Компания ARinteg (ООО «АРинтег») – системный интегратор на российском рынке ИБ –

 Читать далее...

Графические редакторы  

Рисование абстрактных гор в стиле Paper Cut

Векторный графический редактор Inkscape – яркий представитель той прослойки open source, с

 Читать далее...

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

 Читать далее...

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

 Читать далее...

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

 Читать далее...

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 6249
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 6953
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 4243
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 3021
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 3814
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 3833
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6327
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3178
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3470
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7288
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 10653
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12375
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 14012
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9137
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7090
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5398
Комментарии: 3
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4627
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3436
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3167
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3410
Комментарии: 0
Рецензия на книгу «MongoDB в действии»

 Читать далее...

02.12.2013г.
Просмотров: 3034
Комментарии: 0
Не думай о минутах свысока

 Читать далее...

Друзья сайта  

 Работаем с PDF из Perl

Архив номеров / 2006 / Выпуск №3 (40) / Работаем с PDF из Perl

Рубрика: Программирование /  Автоматизация   | Дополнительные материалы

Валентин Синицын

Работаем с PDF из Perl

Бытует мнение, что PDF – это закрытый формат, для работы с которым необходимо дорогостоящее ПО. Это не вполне верно: спецификация PDF доступна всем желающим, а для создания и правки файлов можно применять не только настольные издательские системы, но и сценарии Perl.

Модуль PDF::API2 (http://pdfapi2.sf.net) разрабатывается Альфредом Райбенщухом (Alfred Reibenschuh) и распространяется по лицензии GNU GPL. PDF::API2 написан на чистом Perl и имеет минимум зависимостей: Perl 5.8.4 или выше, Encode и Compress::Zlib (если что-то и придется установить, то только последний из них). Модуль использует стандартную объектную нотацию Perl 5 и доступен как через CPAN, так и через репозитарии ActiveState PPM, поэтому может быть установлен любым удобным для вас способом. Последней на момент написания статьи версией была 0.51.

Поставляемая вместе с PDF::API2 документация насчитывает более 160 страниц, большая часть из которых не имеет практической ценности. Для повседневной работы вам потребуются лишь страницы PDF::API2, ::Content, ::Page и ::Util, где перечислены значения различных констант.

PDF::API2 не является средством для редактирования PDF в прямом смысле этого слова – он не предоставляет специальных методов для «поиска и замены» существующих элементов (например, картинок). Однако с его помощью вы легко можете создавать собственные PDF-файлы, а также обрабатывать и дополнять уже существующие. Как мы вскоре увидим, этого достаточно для решения весьма широкого круга задач. Другое ограничение – невозможность работать с зашифрованными PDF-файлами. Видимых препятствий этому нет – программа дешифровки PDF на Perl (http://www.cs.cmu.edu/~dst/Adobe/Gallery/pdfdecrypt.pl) занимает менее двухсот строк кода. «Альтернативный» модуль CAM::PDF (http://search.cpan.org/~clotho/CAM-PDF-1.05), который, по моему мнению, проигрывает PDF::API2 в удобстве использования, также справляется с этой задачей безо всякого труда. Возможно, этот функционал будет реализован в следующих версиях модуля.

Прежде чем мы приступим к обсуждению констант и функций, давайте поближе познакомимся с форматом PDF версии 1.4, с которой и работает PDF::API2.

Экранная модель PDF

Основой для эффективной работы с PDF является понимание его экранной модели (imaging model). Аналогичные концепции лежат в основе многих современных API для работы с векторной графикой, например, Cairo и Arthur, так что кое-какие представления о них в любом случае окажутся нелишними.

Спецификация PDF 1.4 предусматривает четыре типа объектов, которые могут отображаться на странице: контуры (path object), текст (text object) и растр (image object). Особняком стоит область отсечения (current clipping path) – специальный контур, в пределах которого возможна отрисовка объектов. Любые фигуры или их части, выходящие за пределы данной области, отсекаются, откуда и происходит это название.

Для описания объектов всех четырех типов и работы с ними в PDF служит специальный язык, по своим функциям аналогичный PostScript. Например, для отрисовки контура используется оператор S (stroke), а для его заливки – f (fill) (здесь нет никакой опечатки – операторы «языка PDF» в большинстве своем одно- и двухбуквенные). Как нетрудно видеть, у этих операторов нет параметров. Точки, образующие контур, задаются заранее, а такие атрибуты, как толщина соединяющей их линии, ее цвет и цвет заливки определяются текущим графическим состоянием (graphics state). Одним из наиболее важных элементов этого состояния является матрица преобразования системы координат (current transformation matrix, CTM), определяющая текущее положение декартовых осей. Думается, читатели, имеющие опыт программирования трехмерной графики, испытали чувство дежа-вю, и не зря: аналогия с OpenGL/Direct3D налицо, разве что все преобразования координат в PDF будут двумерными. По умолчанию, система координат имеет начало в левом нижнем углу листа, ось X направлена вправо, ось Y – вверх. За единицу измерения принимается 1/72 дюйма, что примерно соответствует типографскому пункту (pt). Вы можете перенести начало отсчета в другую точку (translate), повернуть оси на некоторый угол (rotate), изменить масштаб (scale) или скосить их (skew). Напомним, что порядок применения этих операций имеет значение: перенос и поворот – это совсем не то же самое, что поворот и перенос. Изменения системы координат непосредственно сказываются на выводимых объектах: так, надпись, расположенная вдоль оси X в повернутой системе координат, будет идти под некоторым углом к краю страницы. Если система координат была перенесена на N пунктов влево и M пунктов вверх, окружность, центр которой якобы находится в начале (0,0), будет изображена в окрестности точки с координатами (M,N) и так далее.

Геометрическая плоскость, на которой введена наша система координат, бесконечна, однако в реальном мире все имеет свой предел. Стандарт PDF предусматривает несколько различных типов границ, наиболее важными из которых являются граница физической страницы (MediaBox) и граница видимой области (CropBox). Каждая из них задается четверкой чисел, представляющих собой координаты левого нижнего и правого верхнего углов прямоугольной области в системе координат по умолчанию. Если CropBox не задан явно, он принимается равным MediaBox.

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

Здравствуй, мир!

По сложившейся традиции, мы начнем свое рассмотрение с программы «Hello, World». Данный пример был взят из стандартного дистрибутива PDF::API2, но слегка модифицирован, чтобы лучше отражать отечественные реалии. Конечно, при практическом наборе кода номера строк (01:, 02:, ...) необходимо опустить; мы также не используем строгий режим (use strict), чтобы слегка уменьшить объем сценариев и повысить их удобочитаемость.

Пример 1

01:#!/usr/bin/perl

02:use PDF::API2;

03:use constant mm => 25.4/72;

04:use constant cm => 2.54/72;

05:use constant in =>    1/72;

06:use constant pt =>    1;

07:$pdf = PDF::API2->new;

08:$fnt = $pdf->corefont('Verdana', -encode => 'cp1251');

09:$page = $pdf->page;

10:$page->mediabox('A4');

11:$gfx = $page->gfx;

12:# Выводим текстовую метку

13:$gfx->textlabel(210/mm/2, 297/mm/2, $fnt, 12, 'Здравствуй, мир !');

14:$pdf->saveas('helloworld1.pdf');

15:$pdf->end;

Строки 1-2, будем надеяться, в особых комментариях не нуждаются. В строках 3-6 задаются стандартные константы, выражающие миллиметр (mm), сантиметр (cm), дюйм (in) и пункт (pt) в принятых в PDF единицах измерения. Рекомендую включать их во все ваши скрипты. Наконец, в строке 7 создается объект PDF::API2, с которым мы и будем работать. Каждому такому объекту может соответствовать не более одного PDF-документа. В строке 8 мы выбираем шрифт, который будет использоваться для вывода текста. Параметр -encode может принимать любое значение, известное модулю Encode вашей инсталляции Perl и, конечно, совпадающее с кодировкой символов в вашем сценарии. Метод corefont указывает, что нам нужен один из «базовых» шрифтов, лицензированных компаниями Adobe и Microsoft для свободного распространения, а посему потенциально доступными на любой системе. Помимо Verdana, сюда входят Georgia, Webdings, Wingdings (набор «Windows Fonts»), Courier, Helvetica, Symbol, Times, ZapfDingbats (набор «Adobe Core Fonts») и их разновидности: жирный, курсив и так далее.

К сожалению, текущая версия PDF::API2 умеет корректно отображать русский текст лишь шрифтами Verdana и Georgia – для остальных гарнитур отсутствует информация о ширине символов, в результате чего буквы слипаются (см. врезку «Изъясняемся по-русски»).

Кроме core-шрифтов, PDF::API2 может использовать произвольные шрифты TrueType, PostScript и BDF, предоставленные пользователем в виде файлов. В этом случае шрифт внедряется в PDF-документ.

В строке 9 мы добавляем в конец нашего, пока еще пустого документа чистую страницу, а в строке 10 устанавливаем ее размер, равный странице формата A4 (210x297 мм) – по умолчанию используется стандартный для США размер Letter. Помимо псевдонима «A4» в методе mediabox можно использовать пару (ширина, высота) или ту самую четверку чисел, о которой говорилось в предыдущем разделе. Подготовительные операции завершаются созданием графического объекта $gfx (можете рассматривать его как холст – canvas) в строке 11.

В строке 13 мы вызываем метод textlabel – это самый простой (но не самый гибкий) способ создания текстового объекта, который будет расположен в точке с координатами 210/mm/2, 297/mm/2 (т.е. в центре листа. Обратите внимание на использование константы mm для перевода миллиметров в пункты) и отрисован шрифтом $font (Verdana) высотой 12 пунктов. В строках 14 и 15 происходит запись PDF-документа на диск и разрушение объекта.

На первый взгляд подготовительный этап может показаться чересчур трудоемким, но давайте не будем спешить и займемся усовершенствованием этого, в общем-то, очень простого примера. Заменим строки 12-13 на:

Пример 2:

01:$n = 10;

02:$R = 50;

# $n текстовых меток по кругу

03:$gfx->translate(210/mm/2, 297/mm/2);

04:for $i (1..$n) {

05:  $gfx->rotate(360/$n);

06:  $gfx->textlabel($R, 0, $fnt, 12, 'Здравствуй, мир !');

07:}

Здесь мы перемещаем начало отсчета в центр листа (строка 3), а затем выводим в цикле 10 текстовых меток, каждый раз поворачивая систему координат на 36 градусов относительно предыдущего положения (строка 5). Что получится в результате? Правильно – десять радиально расходящихся надписей «Здравствуй, мир!». Отступ на $R пунктов по оси X (в повернутой системе координат!) нужен для того, чтобы начальные буквы фразы не накладывались друг на друга (см. рис. 1).

Рисунок 1. Результат работы сценария helloworld1.pl

Рисунок 1. Результат работы сценария helloworld1.pl

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

Вставьте данный кусочек кода после строки 2 в предыдущем примере:

Пример 3

# Синий фон

01:$gfx->fillcolor('blue');

02:$gfx->rectxy($page->get_mediabox);

03:$gfx->fill(1);

# Желтый круг в центре страницы

04:$gfx->fillcolor(‘yellow’);

05:$gfx->circle(210/mm/2, 297/mm/2, 0.95*$R);

06:$gfx->fill(1);

В строке 1 задается одно из свойств текущего графического состояния – цвет заливки (fillcolor). Строка 2 определяет прямоугольник, в точности совпадающий с нашей страницей (поскольку координаты его вершин возвращаются методом get_mediabox). Наконец, в строке 3 дается команда закрасить ранее определенный контур (прямоугольник) цветом, установленным последним вызовом fillcolor (синим). До вызова метода fill наш прямоугольник не отображается на странице. Более того, заменив вызов fill на stroke, мы получим незакрашенный прямоугольник, построенный по тем же самым точкам. Это является следствием того, что операторы S и f не имеют собственных входных параметров, а используют заданные ранее значения. Аналогичным образом в строках 4-6 строится желтый круг радиуса 0.95*$R.

Сохраните сценарий и запустите его на выполнение. Если вы не допустили ошибок при наборе исходного текста, ваши старания будут вознаграждены! На синем небе ярко засияет солнышко, каждый лучик которого шлет привет этому, а может быть, и другим мирам (см. рис. 2).

Рисунок 2. Результат работы сценария helloworld2.pl

Рисунок 2. Результат работы сценария helloworld2.pl

Конечно, возможности PDF::API2 никоим образом не ограничиваются рассмотренными здесь вызовами. Всю необходимую информацию вы можете найти в документации. От себя добавлю, что в сценариях для «серьезной» работы с текстом возможностей, предоставляемых методом textlabel, может оказаться недостаточно. Поэтому если вы намерены составить конкуренцию TeX или Adobe InDesign, советуем ознакомиться со статьей [1], в которой подробно рассмотрен процесс верстки и добавления иллюстраций.

Квитанции, брошюры и все-все-все

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

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

Пример 4

01:#!/usr/bin/perl

02:use PDF::API2;

03:use constant mm => 25.4/72;

04:use constant cm => 2.54/72;

05:use constant in =>    1/72;

06:use constant pt =>    1;

07:$pdf = PDF::API2->open("blank.pdf");

08:$font = $pdf->corefont("Georgia-Italic", -encode => cp1251);

09:$page = $pdf->openpage(1);

10:$gfx = $page->gfx;

11:$gfx->textlabel(3.20/cm, 297/mm-3.30/cm, $font, 12, "Иванов И.И.");

12:$gfx->textlabel(6.0/cm, 297/mm-3.80/cm, $font, 12, "Голодающим детям Африки");

13:$gfx->textlabel(3.05/cm, 297/mm-4.80/cm, $font, 12, "01.01.1970");

14:$pdf->saveas("receipt.pdf");

15:$pdf->end;

Нетрудно видеть, что вместо метода PDF::API2->new здесь используется open, принимающий в качестве параметра имя существующего PDF-файла. Вызов $pdf->page, создающий новую пустую страницу, уступил место методу $pdf->openpage, открывающему уже существующую. Кстати, номер страницы (в нашем случае 1) может быть и отрицательным – это означает, что вы ведете отсчет не с начала документа, а с конца. Файл blank.pdf мы подготовили в обычном текстовом процессоре с возможностью экспорта PDF, а расположение полей для заполнения определили, используя его «линейки» (297мм – высота листа формата A4, отступы отсчитываются от левой и верхней границ страницы) (см. рис. 3).

 Рисунок 3. Заполненная квитанция

Рисунок 3. Заполненная квитанция

Другой весьма широкий класс задач – так называемый пост-процессинг PDF-документов: поворот страниц, обрезание полей (crop), слияние нескольких документов в один и извлечение страниц, а также извечная проблема любителей электронных книг – брошюровка (называемая в народе «верстка книжкой»). Рассмотрим каждую из этих задач по очереди. Во всех последующих сценариях, где это необходимо, подразумевается «преамбула» в объеме строк 1-6 примера 4.

Поворот страниц

01:$pdf = PDF::API2->open("document.pdf");

02:for $i (1..$pdf->pages) {

03:  $pdf->openpage($i)->rotate(90);

04:}

05:$pdf->saveas("document_new.pdf");

06:$pdf->end;

Мы пробегаем в цикле все страницы данного документа (их число возвращает метод pages) и для каждой из них вызываем метод rotate. Отметим, что он отличается от рассмотренного нами ранее метода rotate объекта $page->gfx. Во-первых, здесь допустимы повороты только на углы, кратные 90 градусам. Во-вторых, вызов метода $page->rotate не меняет текущую матрицу преобразования (CTM), а устанавливает особый параметр страницы (можете думать о нем, как о флажке «книжной/альбомной» ориентации) и, таким образом, влияет на вид системы координат по умолчанию. Так, в нашем случае ее начало находится в левом верхнем углу страницы, оси направлены вправо и вниз. Попробуйте нарисовать на каждой странице квадрат с вершинами (0,0) – (100,100), и вы поймете, что имеется в виду.

Обрезание полей

01:$pdf = PDF::API2->open("document.pdf");

02:for $i (1..$pdf->pages) {

03:  $pdf->openpage($i)->cropbox(3/cm, 3/cm, 210/mm-3/cm, 297/mm-3/cm);

04:}

05:$pdf->saveas("document_new.pdf");

06:$pdf->end;

Нетрудно видеть, что решение этой задачи полностью аналогично предыдущему. Всю работу выполняет метод cropbox, «отхватывающий» по 3 сантиметра от каждого края листа формата A4 (в настоящей программе было бы разумно получать текущие размеры каждой страницы при помощи метода get_mediabox). Если сейчас вы подумали об области отсечения (clipping path), то, к сожалению, ошиблись: она здесь ни при чем. Подобно методу rotate, cropbox всего лишь устанавливает определенный параметр в свойствах страницы.

Слияние документов и извлечение страниц

Чтобы решить эту задачу, нам придется поднапрячься и совершить качественный скачок – начать работать с двумя объектами PDF::API2 одновременно. В общем случае нам необходимо создать документ-источник и документ-приемник, а затем перенести страницы с помощью метода importpage. Если документ-приемник ранее не существовал (то есть был создан в процессе работы сценария), мы имеем дело с извлечением страниц, в противном случае налицо (частичное) слияние двух документов.

01:#!/usr/bin/perl

02:use PDF::API2;

03:$source = PDF::API2->open("document.pdf");

04:$dest = PDF::API2->new;

#или PDF::API2->open("document2.pdf");

05:@pages = (1, -1);

06:for $page (@pages) {

07:  $dest->importpage($source, $page, $dest->page);

08:}

09:$dest->saveas("document_new.pdf");

10:$dest->end;

11:$source->end;

В строке 5 мы перечисляем страницы, которые будут импортированы из $source в $dest. В данном случае нас интересуют только первая и последняя. Всю необходимую работу выполняет метод importpage (строка 7), на вход которого подаются объект-источник ($source), номер страницы в исходном документе ($page) и номер страницы в документе-приемнике или объект типа PDF::API2::Page, «в который» будет помещена импортированная страница. Мы используем результат вызова $dest->page, который, напомним, добавляет новую страницу в конец документа $dest. Следует отметить, что созданные таким образом страницы имеют кое-какие ограничения – например, их нельзя импортировать в другой документ (тому есть свои причины) до тех пор, пока они не будут сохранены в реальном файле, а затем заново открыты методом openpage. Если это для вас существенно, создайте пустой одностраничный PDF-документ любым доступным способом (например, в OpenOffice.org) и каждый раз импортируйте его единственную чистую страницу вместо вызова метода page.

Брошюровка

Теперь мы можем перейти к наиболее сложной из заявленных нами задач – брошюровке. Этот процесс состоит из двух этапов. На первом из них создается набор пар – номеров страниц, подлежащих распечатке на одном листе (с двух сторон).

Идея проста: исходный документ дополняется пустыми (или рекламными) страницами так, чтобы их общее число N было кратно 4, затем страницы группируют следующим образом: (N,1), (2, N-1), (N-2, 3) до тех пор, пока первое число в паре меньше второго. Затем каждая пара соответствующим образом уменьшается и копируется на печатный лист.

01:$source = PDF::API2->open("document.pdf");

02:$dest = PDF::API2->new;

03:$left = 1;

04:$right = ($source->pages % 4 == 0) ? $source->pages : $source->pages + (4 - $source->pages % 4);

05:$reversed = 1;

06:while ($left < $right) {

07:  $page = $dest->page;

08:  $page->mediabox(297/mm, 210/mm);

09:  $page->rotate(90);

10:  draw_page($source, $left, $dest, -1, $reversed);

11:  draw_page($source, $right, $dest, -1, !$reversed) if ($right <= $source->pages);

12:  $left++; $right--; $reversed = !$reversed;

13:}

14: $dest->saveas("document_book.pdf");

15:$dest->end;

16:$source->end;

Этот кусочек кода реализует первую половину нашего плана. В переменной $left хранится номер первой, а в $right – второй страницы текущей пары. Строка 4 вкупе с условием if в строке 11 эквивалентна добавлению нужного числа пустых страниц в конец документа $source. Флаг $reversed определяет, где на печатном листе будет расположена страница с меньшим номером – слева или справа. Интерес также представляют строки 7-9: здесь мы добавляем в $dest новую страницу формата «перевернутый A4» и тут же поворачиваем ее на 90 градусов. Таким образом мы получаем обычную страницу формата A4 с необычной системой координат, так что все помещенные на нее объекты будут «лежать на боку». Фактической отрисовкой страницы занимается подпрограмма draw_page, которая принимает пять параметров: объект-источник ($pdf_in) и номер исходной страницы ($in_idx), объект-приемник ($pdf_out) и номер страницы-«печатного листа» ($out_idx), а также позицию на печатном листе (0 – слева, 1 – справа):

01:sub draw_page {

02:  my ($pdf_in, $in_idx, $pdf_out, $out_idx, $position) = @_;

03:  my $xo = $pdf_out->importPageIntoForm($pdf_in, $in_idx);

04:  my $pg_out = $pdf_out->openpage($out_idx); 

05:  if (my $cropbox = $pdf_in->openpage($in_idx)->find_prop("CropBox")) {

06:    $xo->bbox(map {$_->val} $cropbox->elementsof);

07:  }

08:  my @ps = map {$_->val} $xo->{BBox}->elementsof;

09:  my $bbox_width = $ps[2] - $ps[0];

10:  my $bbox_height = $ps[3] - $ps[1];

11:  my (undef, undef, $page_width, $page_height) = $pg_out->get_mediabox;

12:  my $scale_x = $page_width/(2*$bbox_width);

13:  my $scale_y = $page_height/$bbox_height;

14:  my ($scale, $x, $y);

15:  if ($scale_x <= $scale_y) {

16:    $scale = $scale_x;

17:    $x = 0;

18:    $y = ($page_height - $scale*$bbox_height)/2;

19:  }

20:  else {

21:    $scale = $scale_y;

22:    $x = ($page_width/2 - $scale*$bbox_width)/2;

23:    $y = 0;

24:  }

25:  $pg_out->gfx->formimage($xo, $x - $ps[0] + ($position ? $page_width/2 : 0), 26:$y - $ps[1], $scale);

27:}

Метод importPageIntoForm в строке 3 возвращает нужную нам страницу исходного документа в виде «непрозрачного» объекта X-Object. С такими объектами можно выполнять различные преобразования и располагать их в любом месте страницы, но узнать, что находится у них внутри, нельзя. Условие в строках 5-7 выясняет, был ли для исходной страницы установлен CropBox (иными словами – были ли обрезаны поля), и, если это так, создает на основе этой информации область отсечения для объекта X-Object (иначе эта информация будет потеряна – X-Object включает в себя лишь содержимое страницы, но не ее свойства, где, как мы помним, находится поле CropBox). К сожалению, в текущей реализации PDF:API2 не существует метода get_cropbox, поэтому данную информацию приходится извлекать таким «низкоуровневым» способом. В строках 8-10 мы находим ширину и высоту импортируемого объекта и затем вычисляем по ним коэффициенты масштабирования (строки 1113, обратите внимание, что подпрограмма может работать с любыми размерами исходных страниц и печатных листов). Условие в строках 15-24 обеспечивает пропорциональность масштабирования, а метод formimage в строке 25 отображает уменьшенную копию страницы на печатном листе.

Ну вот и все! Теперь осталось только распечатать полученную книжку и отнести в ближайший копи-центр для сшивания, а используя этот сценарий вместе с программой для извлечения страниц, можно распечатать и удобно сшить даже самый монументальный труд. Удачи!

Все обсуждаемые примеры можно загрузить с сайта журнала http://www.samag.ru в разделе «Исходный код».

Приложение

«Изъясняемся по-русски»

«Слипание» букв русского алфавита происходит из-за того, что PDF::API2 не обладает достаточной информацией о их ширине в каждом конкретном шрифте. Существует несколько способов исправить «заморский акцент» PDF::API2:

  • Использовать только core-шрифты Verdana и Georgia – для них эти сведения имеются.
  • Использовать встраиваемые шрифты TrueType или PostScript. Здесь также не исключены проблемы, но они куда менее вероятны – вся информация берется непосредственно из файлов шрифтов.
  • Добавить в файлы PDF/API2/Resource/Font/Corefont/*.pm информацию о ширине символов кириллицы (U+0x04NN). Это не так-то просто, но если вы все же справитесь с этой задачей – не забудьте отправить «заплатку» автору модуля, и благодарное сообщество вас не забудет.

Ссылки:

  1. http://www.printaform.com.au/clients/pdfapi2 – несколько устаревшая, но не потерявшая актуальности статья, детально рассматривающая вопросы верстки текста с помощью PDF::API2.
  2. http://partners.adobe.com/public/developer/pdf/index_reference.html – здесь можно загрузить официальную спецификацию формата PDF от Adobe.
  3. http://www.accesspdf.com/pdftk – домашняя страница PDF Toolkit – открытой программы для обработки PDF-файлов.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru