Генератор PDF документов, основанный на TCPDF/FPDF

Генерацию PDF файлов на сегодня могут предложить несколько известных PHP-библиотек, таких как Zend Framework (Zend_Pdf), FPDF, TCPDF, однако для такой задачи, как "быстрое" создание печатной формы на основе готового PDF-шаблона, ни один из найденных в "чистом виде" не подходит.

Предлагаемый класс CPrintfromPdf служит для автоматизации создания "печатных форм" в формате PDF, на основе подготовленных PDF-шаблонов и пользовательских данных.
Класс является "оберткой" над модулями известного набора TCPDF, (выбранного за наиболее полный функционал и хорошую поддержку), со вспомогательными классами FPDF / FPDFI, необходимыми для импорта "образцовых" PDF-файлов в ккчестве шаблона.

Основные возможности класса:

Установка модуля на web сервер

Кроме собственно модуля printform-pdf.php, который надо скопировать в одну из папок, перечисленных в include_path Ваших php-настроек, необходимо скачать из интернета и установить на сервер следующие бесплатно распространяемые библиотеки:
  1. TCPDF - набор классов для генерации PDF файлов. Ссылка: tcpdf.org
  2. Дополнительные модули FPDF-FPDFI, необходимые для поддержки чтения-импорта PDF шаблонов. Ссылка: http://www.setasign.de/
  3. Если планируется генерация пазлов судоку, нужно скачать и установить модуль class.Sudoku.php
После установки этих модулей на свой сервер, надо сгенерировать и положить в папку tcpdf/fonts комплекты необходимых "шрифтовых" файлов.
Утилита генерации и инструкция по работе с ней может быть найдена в папке fonts/utils комплекта TCPDF.
Учтите, генерировать надо будет комплекты для всех шрифтов, которые вы собираетесь использовать в формируемых PDF файлах. Базовым шрифтом для CPrintformPDF по умолчанию является arial, так что если не планируется менять его на другой, набор шрифтовых файлов arial для Вашего языка должен быть создан и положен в папку tcpdf/fonts в первую очередь.

Использование класса

Ниже представлен простейший пример генерации PDF на основе подготовленного PDF-файла шаблона 'pdf-template.pdf':
if(!class_exists('TCPDF',false)) {
    require_once('tcpdf/config/lang/rus.php'); // Подключаем использование своего языкового пакета, здесь - русского
    require_once 'tcpdf/tcpdf.php';
    require_once 'fpdf/fpdi2tcpdf_bridge.php';
    require_once 'fpdf/fpdi.php';
}
require_once('printform-pdf.php');

$xmlcfg = "pdf-config.xml";

$pdf = new CPrintFormPdf(array(
    'configfile' => $xmlcfg
   ,'outname' => "output-file.pdf"
   ,'author'  => 'Some clever guy'
   ,'creator' => 'Test program'
   ,'title'   => 'Generated test PDF'
   ,'subject' => 'Testing PDF creation filled by user data'
   ,'compression' => true
));

$userdata = array(
    'cityname' => 'Moscow'
   ,'personname' => 'Dow'
   ,'person_firstname' => 'John'
   ,'person_birthdate' => '04/15/1970'
   ,'person_document' => 'Passport 0034 010203'
   ,'demostring' => 'DEMONSTATION!'
);
$pdf->AddData($userdata);
$pdf->Render(); // генерация в поток клиенту, браузер предложит открыть/сохранить PDF файл

Формат настроечного XML-файла

Ниже приводится образец заполненного XML файла с настройками, включая список выводимых полей с данными. (в нашем примере это pdf-config.xml).
<?xml version="1.0" encoding="UTF-8"?>
<printdef>
  <version>1.0</version>
  <title>Testing PDF generation</title>
  <description>This is a test PDF document created by CPrintfromPDF class</description>
  <stringcharset>UTF-8</stringcharset>

  <baseparameters>
    <margins left="0" top="0" right="0" bottom="0" />
    <font name="arial" size="6" />
    <page orientation="P" size="A4" units="mm" />
    <template src="pdf-template.pdf" altsrc="pdf-template-alternate.pdf" />
  </baseparameters>

  <pages>
   <page>
      <template page="1" />
      <field name="cityname" posx="143" posy="26" />
      <field name="personname" posx="42" posy="26" />
      <field name="person_firstname" posx="88" posy="26" width="20" />
      <field name="person_birthdate" posx="42" posy="32" width="70" />
      <field name="person_document" posx="143" posy="32" width="55" />
      <field name="demostring"     posx="50"  posy="110" rotate="45" size="40" color="#EE4040"/>
   </page>
  </pages>
</printdef>
Как видим, XML код в файле конфигурации помимо нескольких основных тегов (title,description, stringcharset) состоит из секций baseparameters и pages.
baseparameters содержит основные параметры страницы, действующие на весь PDF документ.

Тег templatefile в секции "baseparameters" содержит имя исходного PDF файла шаблона, который будет использован как "фон" при генерации конечного документа (атрибут src), и необязательное имя альтернативного PDF шаблона (атрибут altsrc). Для чего это, узнаем позже.
В секции pages задаются блоки <page> ... </page>, хранящие координаты, размеры шрифта и другие параметры для вывода пользовательских данных и файлов изображений на данной странице документа.

Помимо списка полей, блок описания страницы может содеражать один специальный элемент : datagrid, упрощающий вывод однотипных данных в таблицу.
Допустим, вам нужно напечатать на PDF-странице таблицу (до 30 строк) с данными сотрудников, для каждого из них выводя его ФИО, дату рождения и зарплату.
Чтобы сделать это "обычным" способом, пришлось бы готовить длинный XML с описанием всех 30 блоков по 3 поля в каждом :

  ...
  <page>
      <field name="emp_name1" posx="10" posy="26" />
      <field name="emp_birthdate1" posx="50" posy="26" />
      <field name="emp_duty1" posx="80" posy="26" />
      <field name="emp_salary1" posx="120" posy="26" />

      <field name="emp_name2" posx="10" posy="32" />
      <field name="emp_birthdate2" posx="50" posy="32" />
      <field name="emp_duty2" posx="80" posy="32" />
      <field name="emp_salary2" posx="120" posy="32" />
      /**... and so on **/
  </page>
  ...

"datagrid" позволяет сдлеать это "немного" проще:
  ...
  <page>
      <field name="emp_name" posx="10" />
      <field name="emp_birthdate" posx="50" />
      <field name="emp_duty" posx="80" />
      <field name="emp_salary" posx="120" />
      <datagrid name="empl_grid" fields="emp_name,emp_birthdate,emp_duty,emp_salary" posy="26" step_y="6" rows="30" />
  </page>
  ...
В теге "datagrid" задаются следующие атрибуты : Помните, что в XML файле описания тег datagrid должен быть ПОСЛЕ описаний полей, которые в него войдут !

В скрипте генерации PDF документа, чтобы заполнить "грид" данными, можно воспользоваться любым из способов:
  1. Честно заполнить массив данных для метода AddData($data) всеми "нумерованными" парами данных ключ=>значение:
    $data[emp_name1'] = 'Joshua Gordon';
    $data['emp_birth1'] = '1980-04-20';
    ...
    $data['emp_name2'] = 'Alex Johnson';
    $data['emp_birth2'] = '1975-10-09';
    ...
    $pdf->AddData($data);
    
  2. Занести в основной массив $data все поля кроме таблицы, вызвать метод AddData($data), а после этого вызвать метод AddDataGridRow():

    // мы считаем, что каждый элемент массива  $employees - это ассоциативный массив с необходимыми значениями
    $pdf->AddData($data);
    foreach($employees as $oneempl) {
      $pdf->AddDataGridRow('empl_grid', $oneempl);
    }
    
    AddDataGridRow() заполняет одну строку таблицы и может быть вызван столько раз, сколько у вас есть фактических данных (в нашем примере - сотрудников).
  3. Все данные для "грида" могут быть переданы как особый элемент массива $data для метода AddData().
    Для этого используется специальное имя (ключ) поля в массиве, оно начинается с префикса 'grid:', за которым следует имя (ИД) нашего описания таблицы:
    $data['grid:empl_grid'] = array(
         array('emp_name'=>'Joshua Gordon', 'emp_birth'=>'1980-04-20')
        ,array('emp_name'=>'Alex Johnson', 'emp_birth'=>'1975-10-09')
    );
    $pdf->AddData($data);
    $pdf->Render();
      
Во всех трех случаях результат будет одинаковый.

Дополнительные возможности

Кроме собственно "форматированного вывода", в класс CPRintformPdf были добавлены функции печати "специальных" страниц :

Описание методов класса

Конструктор класса может принимать следующие параметры:

CPrintformPDF($param='', $data=null, $outname='', $tofile=false),
$param может строкой с именем XML-файла конфигурации ЛИБО ассоциативным массивом ('ключ'=>значение) :
КлючНазначениезначение по умолчанию
template Имя файла шаблона --
dataассоциативный массив пользовательских данных--
outnameимя выходного PDF файла--
compression включает режим генерации компрессированного PDF (в PHP должна быть включена поддержка функций ZIP !) false
tofile если указать любое непустое значение, сгенерированный PDF документ будет сохранен (на сервере) в файл с именем, указанным в параметре outname false
configfile передает имя XML-файла конфигурации  
subject строка для поля "Тема" (subject) в PDF документе пустая строка
author строка для поля "Автор" (Author) в PDF документе CPrintFormPdf, TCPDF wrapper class
creator строка для поля "Кем создано" (Creator) в PDF документе printform-pdf module ...
stringcharset Кодировка исходных строковых значений в массиве переданных данных. Нужна, чтобы корректно вывести строки имевшие кодировку отличную от UTF-8, и должна быть одной из поддерживаемых PHP-функцией iconv(). Кодировку можно не задавать, если выводимые данные будут передаваться в UTF-8 ''
Ниже - полное описание всех настроечных параметров XML файла конфигурации, (либо строки, содержащей XML код), передаваемого в конструктор класса.

ПараметрНазначениеЗначение по умолчанию
stringcharsetКодировка строк в пользовательских данных (чтобы корректно конвертировать в Unicode, применяемый в PDF)""
titleЗаголовок документа (данные для соотв.атрибута PDF файла)""
descriptionОписание документа (данные для соотв.атрибута PDF файла)""
pageОбщие Параметры страницы:
orientationориентация листа : 'P' (портретная) либо 'L' - ландшафтная'P'
sizeРазмер листа ('A4', 'A5' и т.д.)'A4'
unitsВ чем задаются координаты, размеры (высота, ширина)'mm'
Секция "baseparameters"
fontЗадание параметров базового шрифта (используется для полей, у которых атрибут font не указан)
Пример: <font name="verdana" size="3.5" />
атрибут nameимя базового шрифтаarial
атрибут sizeразмер базового шрифта, в юнитах (mm) 4
marginsОтступы от краев листа
Пример: <margins left="10" top="5" right="10" bottom="2" />
атрибуты left,right, top, bottomотступы слева, справа, сверху и снизу0
templatefile Атрибут "src" передает имя основного PDF файла шаблона для всех страниц ''
Секция "pages" - включает набор блоков <page> ... </page> - каждый задает список полей на одной странице
templateТег <template> задает PDF файл, используемый в качестве шаблона (бланка) страницы
Пример: <template src="template.pdf" page="1" />
атрибут srcимя (с путем) PDF файла. Если не указан, используется основной файл шаблона, указанный в секции baseparammeters
атрибут pageУказывает номер страницы в данном PDF, используемой в качестве шаблона (нумерация начинается с 1).
Если используется PDF-шаблон, явное указание номера "копируемой" из него страницы обязательно (и не должно быть нулем)
0
fieldЗадание параметров одного выводимого поля пользовательских данных
Пример: <field name="cityname" posx="143" posy="26" />
атрибут nameимя поля: если в ассоциативном массиве пользователских данных есть элемент с соответствующим ключом, его значение выводится в указанных позициях страницы--
атрибут type"Тип" поля, задающий специфические лбъекты на PDF форме:
атрибут type="image" задает вывод изображения из файла (тег src указывает имя исходного файла),
type="checkbox" - при непустом значении переданных пользовательксих данных будет выводится символ "включенного" флажка (чек-бокса), при нуле - не выводится ничего.
type="poly" позволяет вывести открытый полигон (ломаную линию), задав списки координат ее узловых точек. Атрибуты posx, posy должны содержать одинакокове число значений - позиций узловых точек полигона. (Если число Nx координат X меньше или больше чем Ny - координат Y, "лишние" значения игнорируются).
type="qrcode", type="barcode" - задают печать штрих-кодов и QR-кодов (см. подробнее ниже
--
атрибуты posx, posyЗадают позицию вывода на странице, в текущих юнитах (mm)
Атрибут posx может представлять список значений через запятую - подробнее см. ниже.
0,0
атрибуты width, heightЗадают предельную ширину и высоту для текстового значения. При необходимости выполняется авто-перенос длинных строк, чтобы оставаться в пределах указанной ширины, и обрезание текста при превышении высоты Для поля типа image задают ширину и высоту изображения (выполняется авто-масштабирование исходного файла).
Внимание: Если заданы оба параметра и их соотношение не равно соотношению ширины и высоты переданной картинки, при отображении в PDF она будет растянута или сжата.
0,0
атрибут charstepЗадает шаг (в текущих юнитах) между выводимыми символами строкового значения. При наличии этого атрибута строка данных разбивается на символы и они выводятся с указанным шагом по горизонтали. Полезно для точного вывода в напечатанные "квадратики" готового бланка, расчитанного на ручное "поклеточное" заполнение0
атрибут maxlengthОграничивает длину выводимой строки. Все символы после "maxlength"-го не будут напечатаны
атрибут fontЗадает имя шрифта. Если не указан или пустая строка, используется "базовый" шрифт
атрибут sizeЗадает размер шрифта. Если не указан или пустая строка, используется "базовый" размер0
атрибут colorЦвет символов. Может передаваться в любой нотации, поддерживаемой в TCPDF ("rgb(n1,n2,n3)", базовое название цвета, 3х- или 6-значный Hex-код). Если не указан или пустая строка, используется черный
Можно задать имя функции (предварив знаком "@"), чтобы цвет формировался "на лету" : "@MyColorFunction"
атрибут bgcolorЦвет фона под выводимыми данными. Может передаваться в любой нотации, поддерживаемой в TCPDF.
Можно задать имя функции (предварив знаком "@"), чтобы цвет формировался "на лету" : "@MyColorFunction". При указании цвета фона рисуется прямоугольник заданного цвета, а уже поверх него выводится собственно значение данных. Поэтому важно, чтобы вместе с атрибутом bgcolor были переданы width и height, иначе ширина и высота прямоугольника будет считаться "нулевой".
Этим атрибутом можно пользоваться, если надо закрасить некую область страницы и поверх вывести свой текст.
атрибут alignВыравнивание текста по горизонтали. Возможные значения (L,R,C,J) - в соответствии с требованиями к параметру align в методах класса TCPDF
атрибут optionsСтрока дополнительных опций, интерпретируемых в зависимости от типа поля
Можно задать имя функции (предварив знаком "@"), тогда набор "опций" будет формироваться "на лету", непосредственно перед выводом в PDF : "@MyOptionsFunction"
--
атрибут srcимя (с путем) графического файла, для поля иипа "image". О поддерживаемых типах файлов см. документацию к классу TCPDF
атрибут rotateЗадает угол поворта текста/картинки/штрих-кода (в град., против часовой стрелки)0, 0


Одно значение пользовательских данных может быть выведено на странице неоднократно, для чего достаточно в XML-описании внести несколько блоков <field ... > с одним и тем же именем поля (атрибут name).

Использование массива координат posx

Как уже упоминалось, с помощью атрибута charstep можно выполнить "разреженный" вывод символов строки, с указанным шагом в миллиметрах между буквами. Однако может возникнуть необходимость вывода с "переменным" шагом или даже с пропуском определенных символов в строке. Например, мы хотим вывести дату, переданную в формате "ДД.ММ.ГГГГ" в шесть "квадратиков, избегая точек и беря от года только две последние цифры. В этом случае значение posx задается примерно в таком виде:
<field name="date_entered" ... posx="50,54,0,70,74,0,0,0,90,94" />


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

LoadConfig([$cfgname]) - загружает конфигурацию из файла с указанным именем. Если имя XML файла не было передано в конструктор класса, то здесь можно загрузить описание формата "явным" способом (после вызова метода-конструктора). Вместо имени файла можно также передать готовую XML-строку.

addPageDef($opts=array()) - программное добавление описания страницы (в дополнение к настроечному XML файлу). Нужно приготовить ассоциативный массив со всеми необходимыми параметрами, и передеать его в функцию:
$opts['template'] - ассоциативный массив, описывающий source PDF file parameters to use as "template" for the page (See template attributes for all supported values) $opts['fields'] - ассоциативный массив, описывающий all fields on this page (See "field" attributes for all supported values)
$opts['repeat'] - массив, описывающий "повторение" данных на странице (эквивалентен тегу "repeat" в настроечном XML файле)

AddFieldDefinition($ipage, $parm) программно добавляет описание одного выводимого поля данных для страницы с заданным номером.
$ipage - номер страницы, $parm - ассоциативный массив с описанием поля, где ключи элементов должны точно соответствовать перечисленным выше атрибутам тега field - posx, posy, type, и т.д.

setTitle($strg) задает значение для атрибута Title (заголовок) в выходном PDF.

setSubject($strg) задает значение для атрибута Subject (тема).

setAuthor($strg) задает значение для атрибута Author (автор).

setCreator($strg) задает значение для атрибута Creator.

disableImages($par=true) - полностью отключает вывод всех изображений в документе. Полезно, например, если в режиме тестирования нужно спрятать оттистки печати, факсимиле подписи руководителя и т.д.

AddData($entitydata) добавление массива данных для печати страницы (или блока страниц, если текущее XML-описание задает многстраничный документ).
Возможен многократный вызов метода AddData() перед финальным вызовом Render() :

каждый вызов метода AddData() означает добавление новой страницы либо блока страниц в генерируемый документ. Т.е. один вызов AddData() соответствует одному полному документу. Каждый последующий вызов будет добавлять новый "полный" документ в генерируемый файл.

Таким образом с помощью одного вызова метода Render() можно создать PDF файл, содержащий несколько экземпляров готовых документов - например, заполненные договоры для нескольких человек, чьи данные были переданы в нескольких вызовах AddData().
Массив передаваемых данных должен иметь ту же структуру, что и в конструкторе класса (ассоциативный массив с ключами, соответствующими именам полей).

DrawMeasuringGrid($step = 10, $color = false) - отладочная функция : в процессе настройки вывода полей на бланк (координат и размеров областей печати) может пригодиться координатная сетка, которая распечатывается поверх текущего бланка.
Данный метод инициирует ее вывод, с указанным шагом $step, и цветом $color. Если шаг не указан, используется 10мм, если указано значение "1" или TRUE, это интерпретируется не как 1 мм, а "да" - печатать сетку, и шаг также будет "дефолтным" 10мм. Цвет сетки по умолчанию - серый (RGB: 80,80,80). Цвет можно передать как массив из трех Int-значений (R,G,B), либо одно целочисленное значение N (получится монохромный RGB(N,N,N)), либо в CSS-нотации ("#rrggbb","#rgb").

addPluginData($name, $data) : передает данные, которые должны быть "отрисованы" с помощью плагина. Подробности см. в описании плагинов.

setResourcePaths($pdfPath=null, $imgPath=null) : по умолчанию printformPdf ищет все PDF шаблоны и файлы картинок от текущей папки. Но иногда эти "ресурсы" могут храниться в отдельной папке, например в "resources/" . Если вы не хотите в каждом атрибуте "src" писать одни и те же длинные пути, можно воспользоваться данной функцией и один раз указать точное местоположение файлов.
Первый параметр $pdfPath задает папку для PDF шаблонов, а $imgPath - папку для исходных изображений.

Render($output=true) : метод выполняет собственно генерацию выходного PDF файла, и, по умолчанию, тут же выдает его содержимое в "поток" клиенту (либо, в зависимости от режима вывода, сохраняет на сервере, под заданным именем). Если передать параметр $output равный нулю (или FALSE), вывод/сохранение не производится, и объект остается открытым для дальнейших операций записи. Это может быть нужно, если после генерации "основной части" вы хотите что-либо добавить прямыми вызовами методов класса TCPDF (и только потом сохранить конечный результат). Для получения TCPDF-"хендлера" (точнее, ссылки на TCPDF-объект) служит метод getPdfObject(). По окончании собственных манипуляций, для вывода/сохранения результата остается самостоятельно вызвать метод Output() (не забывайте делать это, иначе никакого PDF создано не будет).
// Нарисуем горизонтальную линию
$pdf = new CPrintFormPdf(...);
...
$pdf->AddData($mydata);
$pdf->Render(false);
$tcpdf = $pdf->getPdfObject();
$tcpdf->Line(10,100,190, 100);
$pdf->Output(); // финал: закрываем PDF и посылаем клиенту (или сохраняем на диск)

Вывод штрих-кодов

Благодаря тому, что классы TCPDF поддерживают печать штрих-кодов многих типов, в настоящем классе также реализована такая функция, при помощи описания поля с типом "barcode"
Фрагмента XML с описанием поля "штрих-код"
      ...
      <field name="barcode_crc" type="barcode:C128" posx="10"  posy="180" width="40" height="10" options="border,text" color="#EE4040"/>
      ...
В атрибуте type="..." указывается тип "barcode", далее - любой символ разделителя (у нас в примере - двоеточие) и тип желаемого штрих-кода. Тип должен быть одним из имен, поддерживаемых классами TCPDF. Если тип не указан, по умолчанию принимается "C39".
Для версии TCPDF 5.9.109 допустимы следующие типы штрихкода (список неполный, приводится для примера) :
C39, C39+, C39E, C39E+, C93, S25, S25+, I25, I25+, C128, C128A, C128B, C128C, EAN2, EAN5, EAN8, ...

Полный список поддерживаемых типов смотрите в документации и исходных кодах комплекта TCPDF.

Атрибут options для штрих-кода должен быть строкой, содержащей разделенные запятой необходимые значения из следующего списка:
Названиеназначение
borderВключает рисование рамки вокруг штрих-кода
textВключает вывод текста с исходным кодом внизу штриховой области (по умолчанию печатается только штрих-код)
Атрибуты color, rotate, width, height влияют на выводимый штрих-код очевидным образом. При задании ширины (width) надо иметь в виду, что при слишком малой ширине и большом кодируемом тексте штрих-код может оказаться нечитаемым сканерами, из-за потери качества.

Вывод QR-кодов

При задании в описании поля атрибута "type" равным "qrcode", значение данного поля кодируется в QRcode, размерами width x height, атрибуты цвета и поворота (color, rotate) так же влияют на вывод, как и в случае штрих-кодов. Для QRcode также надо задавать минимальные размеры области кода с учетом длины кодируемой строки данных, иначе можно получить "битый" (нечитаемый) код.

Размножение (дублирование) данных на странице : тег repeat

Что если вам нужно на одной странице многократно вывести один и тот же блок данных ? Наглядный пример - печать визитных карточек. Конечно, можно создать описание, где будут повторены все блоки полей, с соответствующей подгонкой координат вывода. Неудобства такого подхода очевидны: чтобы передвинуть или добавить одно поле, нужно менять (добавлять) его описание во всех дублирующих блоках описания, пересчитывать координаты вывода с учетом смещения от "первого экземпляра" выведенных данных.

Для решения этой задачи есть специальный тег repeat, задающий смещение по горизонтали (offset_x) и вертикали (offset_y) по отношению к координатам "исходного" блока данных.

Как работает repeat ?
Все элементы вывода (текстовые поля, штрих-коды, изображения), описанные на данной странице, считаются одним блоком данных, и он полностью выводится на этой же странице, с указанным смещением по X и Y. Допустимо на одной странице задавать несколько тегов repeat, чтобы сделать несколько копий блока данных. Ниже приводится пример XML-описания для печати визитных карточек, включая служебные "уголки", по которым потом следует разрезать лист.

Примечение: Чтобы исключить какое-либо поле из "повтора", в список его атрибутов (в описании этого поля) нужно добавить norepeat="1".
<?xml version="1.0" encoding="UTF-8"?>
<printdef>
  <version modified="2012-11-02">1.0</version>
  <title>Visit card template</title>
  <baseparameters>
    <margins left="0" top="0" right="0" bottom="0" />
    <font name="arial" size="8" />
    <page orientation="P" size="A4" units="mm" />
  </baseparameters>

  <pages>
   <page>
   <template page="1" />
    <field name="corner1" type="poly" posx="10,10,14" posy="14,10,10" color="#AAA" />
    <field name="corner2" type="poly" posx="91,95,95" posy="10,10,14" color="#AAA" />
    <field name="corner3" type="poly" posx="10,10,14" posy="61,65,65" color="#AAA" />
    <field name="corner4" type="poly" posx="91,95,95" posy="65,65,61" color="#AAA" />

    <field name="logo" type="image" posx="12" posy="12" width="26" height="26" src="logo.png" />
    <field name="lastname" posx="45" posy="20" width="35" color="#008" size="14" />
    <field name="firstname" posx="45" posy="28" width="35" size="12"/>
    <field name="middlename" posx="45" posy="35" width="35"  size="12" />
    <field name="duty"       posx="45" posy="42" width="35" size="7"/>
    <field name="phones"     posx="15" posy="52" width="75" align="C" size="7"/>
    <!-- duplicate card on the page -->
    <repeat offset_x="88" offset_y="0" />
    <repeat offset_x="0" offset_y="57" />
    <repeat offset_x="88" offset_y="57" />
    <repeat offset_x="0" offset_y="114" />
    <repeat offset_x="88" offset_y="114" />
    <repeat offset_x="0" offset_y="171" />
    <repeat offset_x="88" offset_y="171" />
    <repeat offset_x="0" offset_y="228" />
    <repeat offset_x="88" offset_y="228" />

   </page>
  </pages>
</printdef>
Подготовив такое описание в XML файле (с именем, допустим, "pdf-card.xml"), мы легко можем вывести нашу визитную карточку со своими данными:
(В нашем случае в качестве логотипа компании выводится файл "logo.png" - убедитесь, что он есть в текущей папке!)
$pdfOptions = array('outname' =>'mycard.pdf', 'configfile' => 'pdf-card.xml');
$pdf = new CPrintFormPdf($pdfOptions);
$data = array(
     'lastname'=>'Stivenson'
    ,'firstname'=>'Alex'
    ,'middlename'=>'Jr'
    ,'duty'=>'Senior Sales Manager'
    ,'phones'=>'+1(555)111-2200, 111-2233'
);
$pdf->AddData($data);
$pdf->Render();

Поля, выводимые на всех страницах

Иногда возникает нужда вывести один и тот же текст, картинку, штрих-код на всех станицах документа - в одни и те же координаты, одним и тем же цветом, шрифтом т т.д. Конечно, можно в описание каждой страницы воткнуть один и тот же тег <field>. Но очевидно, было бы удобнее задать параметры такого вывода один раз. Для этого введена поддержка секции "allpages". Она располагается внутри корневого элемента XML файла и содержит стандартные описания полей - в тегах <field>
Ниже пример оформления (внизу всех страниц выводим штрих-код):
<?xml version="1.0" encoding="UTF-8"?>
<printdef>
  <version>1.0</version>
  <title>Testing PDF generation</title>
  <allpages>
    <field name="bar_code01" type="barcode" posx="100" posy="280" width="50" height="5" options="text" value="012345678" />
  </allpages>
  ...
</printdef>
Единственное отличие у тега field в данном случае: в атрибуте value можно сразу указать, какой текст (значение) будет выводиться в данном поле. В любой момент его можно будет изменить с помощью вызова метода setAllPagesFieldValue(), либо просто задав другое значение в наборе данных, передаваемых в методе addData(). Учтите, что поле с таким же именем в наборе данных будет иметь приоритет над значением, заданным в атрибуте value (или установленным с помощью setAllPagesFieldValue() ).

Есть также public метод addAllPagesField($fdef, $value=null), вызов которого эквивалентен наличию секции field в блоке allpages XML-файла настроек.

Поддержка плагинов

Начиная с версии 1.1 printform-pdf поддерживает подключение плагинов. Для этого объявлен новый абстрактный класс, PfPdfPlugin, который является "скелетом" всех плагинов для класса CPrintformPdf.

В контексте работы в CPrintformPdf плагин выполняет некую "сложную" отрисовку внутри прямоуголной области, задаваемой начальными координатами, шириной и высотой. В классе плагина, (наследующем от абстрактного PfPdfPlugin), вы должны написать реализацию двух методов:

__construct($tcpdfobj, $cfg = null, $x=0, $y=0, $w=0, $h=0) - конструктор класса, который должен принимать в качестве параметров ссылку на объект TCPDF (через который он будет выводить все примитивы на PDF-страницу), массив настроечных параметров ($cfg), и координаты и размеры области вывода ($x,$y - начальные координаты, $w - ширина, $h - высота, в миллиметрах).

Render($data) - основной метод, который собственно и производит вывод в в пределах указанной области. $data - пользовательские данные, которые предполагается "визуализировать".

Для корректной работы плагина, его надо подключать в своем коде ПОСЛЕ модуля printform-pdf.php:
require_once('printform-pdf.php');
require_once('my-plugin.php');
Далее, нужно добавить в настроечный XML-файл описание "поля" типа "plugin" :
<?xml version="1.0" encoding="UTF-8"?>
<printdef>
  ...
  <pages>
   <page>
      <template page="1" />
      <field name="cityname" posx="143" posy="26" />
      <field name="personname" posx="42" posy="26" />
      <field name="person_firstname" posx="88" posy="26" width="20" />
      <field name="person_birthdate" posx="42" posy="32" width="70" />
      <field name="person_document" posx="143" posy="32" width="55" />
      <plugin name="chart_gantt" type="my_plugin_classname" posx="10"  posy="160" width="180" height="80"
        options="dateformat=d.m,descr_width=0.25,arrow_color=#F22,shade_color=#BBB" />
   </page>
  </pages>
</printdef>
Заметьте, что значение атрибута "type" ("my_plugin_classname" в нашем случае) должно быть именем нашего класса, наследующего от PfPdfPlugin. CPrintformPDF считает элемент "plugin" специфическим полем вывода (вот почему его описание находится среди других полей внутри области page) :
вывод этого "поля" осуществляется в том порядке, в каком оно находится в списке полей, и к нему применяются значения необязательных атрибутов "bgcolor" и "rotate" (таким образом, плагин будет выводить поверх закрашенной области, и с нужным поворотом, если он задан).

Наконец, где-то в коде вывода PDF-документа нужно подготовить данные для плагина и передать их в объект CPrintformPdf с помощью метода setPluginData($name, $data), ДО вызова финального CPrintformPdf::Render().

$name должно соответствовать имени нашего "псевдо-поля" plugin-типа, как оно указано в атрибуте "name" attribute в теге "plugin".
$data массив (или объект) с вашими данными. Его тип и структура полностью зависят от вас, т.к. это ваш плагин, не так ли ?

Когда доходит очередь до печати Plugin-поля, CPrintformPdf пытается создать объект вашего класса, передав в конструктор экземпляр TCPDF, текущие параметры конфигурации и данные для визуализации.

Параметры конфигурации - это ассоциативный массив, который создается как комбинация текущих настроечных параметров экземпляра CPRintformPdf (включая значение кодовой страницы входных строк - "stringcharset") и опций, заданных в атрибуте "options" тега plugin. Значение атрибута "options" должно быть набором разделенных запятыми пар вида "optname=value". Эта строка разбирается и заносится в массив соответствующими парами ключ-значение ($options = array('optname => 'value',...) ), и итоговый массив передается в конструктор плагина.

Ниже приведен пример реализации простого плагина, рисующего прямоугольную рамку.
class PfPdf_Gantt extends PfPdfPlugin {
    private $_config = array();
    private $tcpdf_obj = null;
    private $xpos,$ypos,$width,$height;
    public function __construct($pdfobj, $config=null, $x=0,$y=0,$w=0,$h=0) {
        $this->tcpdf_obj = $pdfobj;
        $this->xpos = $x;
        $this->ypos = $x;
        $this->width = $w;
        $this->height = $h;
        $this->_config = $config;
    }
    public function Render($data) { // just draw a rectangle
        $this->tcpdf_obj->Rect($this->xpos,$this->ypos,$this->width,$this->height,'D');
    }
}

Реальный рабочий плагина можно найти на этом сайте, поискав по строке "pdf_gantt". Это класс, выводящий графики Ганта, он может работать как самостоятельный модуль (вместе с TCPDF!), так и в качестве плагина класса CPrintformPdf.

Бонус-Функции



AddPageLined($title='',$options=null) создает чистую "разлинованную" страницу (в горизонтальную/вертикальную линейку или в клетку) с заданными параметрами.

$title - необязательный заголовок, печатается вверху страницы.

$options : если передан, должен быть ассоциативным массивом, ниже список возможных элементов:
ЭлементНазначение
$options['step_y'] шаг сетки по вертикали (указывается только в мм). Если не передан, принимается значение 5 мм.
$options['step_x'] шаг сетки по горизонтали. Если не передан, приравнивается к шагу по вертикали. Чтобы явно задать режим "только горизонтальные линейки", надо явно задать нулевое значение для step_x. (Аналогично можно заказать печать только вертикальных линеек)
$options['color'] цвет линий. По умолчанию - серый, RGB(180,180,180)


Пример печати клетчатого листа:
$pdf = new PrintFormPdf;
$pdf->AddPageLined('Lined page title',
    array(
        'step_y'=>12
       ,'step_x'=>12
       ,'color'=>'red'
   )
);


Есть еще один способ применения метода AddPageLined() - вывод "миллиметровой бумаги". Для этого достаточно вызвать метод с такими параметрами:

$obj->AddPageLined('','mm'); // выводится "серая" миллиметровка, цвет линий - RGB(180,180,180) // или другой способ, если нужно задать свой цвет миллиметровой сетки: $obj->AddPageLined('', array('mm'=>true, 'color'=>array(20,200,20)));


AddPageMusicStaff($title='',$options=null) рисует чистый нотный лист.
При вызове без параметров выводится лист без заголовка, содержащий 10 отдельных нотных станов (цвет линий - черный) без тактовых линеек-разделителей.

$title - заголовок страницы
Массив $options:
ЭлементНазначение
$options['merged_staves']кол-во "соединенных" нотных станов. При указании ненулевого числа N нотные станы соединяются в блоки по N шт.
$options['measures']кол-во тактов в строке. При заданном непустом значении от 2х и выше будут выводиться "тактовые черты", делящие стан на соответствующее число тактов.
$options['step_roll']Вертикальный шаг, с которым выводятся нотные станы (расстояние между первыми линейками соседних нотных станов), мм. По умолчанию - 27 мм.
$options['step_line']Расстояние между соседними линейками одного нотного стана, мм. По умолчанию - 2 мм.
$options['accolade']Ненулевое значение включит вывод акколады, объединяющей нотные станы одной группы (только при параметре merged_staves больше 1)
$options['color']Цвет линий. По умолчанию - черный, RGB(0,0,0)


AddPageSudoku($title='', $level=false, $options=null) выводит страницу паззлов Судоку, в зависимости от переданных параметров выводится либо чистый шаблон, либо сгенерированные случайным образом пазлы заданного уровня сложности. Для генерации пазла используется класс class.Sudoku.php автора Richard Munroe.
$title - необязательный заголовок страницы.
$level - уровень сложности генерируемых головоломок, целое число от 1 до 10. Если параметр не передан или нулевое значение, выводятся чистые "бланки" пазлов. Ввиду особенностей алгоритма генерации Судоку некоторые блоки пазла могут оказаться незаполненными (если не удалось сформировать комбинацию "открытых" ячеек за предельное число итераций).
Необязательный третий параметр, ассоциативный массив $options можно использовать для настройки цвета:

ЭлементНазначениеПо умолчанию
$options['color_grid']цвет линий, формирующих поле головоломки.черный - RGB(0,0,0)
$options['color_cell']цвет цифр в ячейкахчерный - RGB(0,0,0)

Возможные проблемы

1. Не удается загрузить исходный PDF-шаблон. TCPDF выводит предупреждение.
Скорее всего, вы используете исходный файл, сжатый одним из алгоритмов, не поддерживаемых классами FPDF / FPDFI. Модули на PHP очевидно могут "не понимать" какие-то новые "фичи" формата PDF. Сохраните свой шаблонный PDF в режиме без сжатия.

История изменений

1.4.0023 (16.04.2014)
1.3.0020 (2014-02-01)
1.2 (2013-12-10)
1.1.0017 (22.05.2013)
1.1 (26.04.2013)
1.00 (14.11.2012)


Распространяется по лицензии: BSD License


Copyright © 2012-2013 Alexander Selifonov, www.selifan.ru