Динамическое создание PDF-файлов при помощи PHP

Управление форматом и содержимым PDF-файлов при помощи PHP

Пройдите через весь процесс динамического создания PDF-файлов с использованием PHP. Поэкспериментируйте с инструментальными программами с открытыми исходными кодами, такими как библиотека Free PDF (FPDF) или PDFLib-Lite, и PHP-кодом для управления PDF-форматом вашего содержимого.

Джек Д Херрингтон, главный инженер-программист, Leverage Software Inc.

Джек Д. Херрингтон (Jack D. Herrington) - главный инженер-программист с более чем двадцатилетним опытом работы. Он автор трех книг: "Генерирование кода в действии", "Podcasting Hacks" и "PHP Hacks". Написал более 30 статей. Вы можете связаться с Джеком по адресу jherr@pobox.com.



11.05.2012

Иногда возникает необходимость управлять подготовкой страниц к выводу на печать. В этой ситуации HTML не является лучшим выбором. PDF-файлы предоставляют полный контроль над визуализацией текста и графическими изображениями на странице. К сожалению, API для создания PDF-файлов не входит в стандартный инструментарий PHP. Мы поможем решить эту проблему.

Часто используемые сокращения

  • API: Application Programming Interface (прикладной интерфейс программирования)
  • DOM: Document Object Model (объектная модель документа)
  • HTML: Hypertext Markup Language (язык разметки гипертекста)
  • PDF: Portable Document Format (переносимый формат документов)
  • W3C: World Wide Web Consortium (консорциум WWW)
  • XML: Extensible Markup Language (расширяемый язык разметки)

Первым, что вы найдете в Интернете при поиске поддержки PDF для PHP, будет, скорее всего, коммерческая библиотека PDFLib и ее версия с открытыми исходными кодами PDFLib-Lite. Это хорошие библиотеки, но коммерческая версия стоит довольно дорого. Облегченная версия распространяется только в исходных кодах, и это ограничение может оказаться проблемой при попытке установить ее в рабочей среде.

Еще одним вариантом является библиотека Free PDF (FPDF), встроенная в PHP. Она не требует компиляции и совершенно бесплатна, поэтому не создает водяных знаков, как нелицензированная версия PDFLib. Именно библиотеку Free PDF я использую в данной статье.

Для демонстрации динамического создания PDF-файлов мы будем использовать итоговую таблицу женских соревнований по роликовым конькам. Эта таблица была извлечена из Интернета и преобразована в XML. В листинге 1 приведен пример XML-файла с данными.

Листинг 1. XML-данные
<events>  
  <event name='Beast of the East 2011'>
    <game score1='88' team1='Toronto Gore-Gore Rollergirls' 
          team2='Montreal La Racaille' score2='11'/>
    <game score1='58' team1='Toronto Death Track Dolls' 
          team2='Montreal Les Contrabanditas' score2='49'/>
     ...
  </event>
  <event name='Dustbowl Invitational 2011'>
     ...
  </event>
  <event name='The Great Yorkshire Showdown 2011'>
     ...
  </event>
</events>

Корневым элементом XML является тег events. Данные сгруппированы по событиям, где каждое событие содержит несколько матчей. В теге events содержится ряд тегов event, в которых, в свою очередь, находятся несколько тегов game. Теги game содержат названия двух команд-участниц и счет матча.

В листинге 2 приведен PHP-код для чтения этого XML.

Листинг 2. getresults.php
<?php
function getResults() {
  $xml = new DOMDocument(); 
  $xml->load('events.xml'); 
  $events = array();
  foreach($xml->getElementsByTagName('event') as $event) { 
    $games = array();
    foreach($event->getElementsByTagName('game') as $game) {
      $games []= array( 'team1' => $game->getAttribute('team1'),
        'score1' => $game->getAttribute('score1'),
        'team2' => $game->getAttribute('team2'),
        'score2' => $game->getAttribute('score2') );
    }
    $events []= array( 'name' => $event->getAttribute('name'),
      'games' => $games );
  }
  return $events;
}
?>

Этот сценарий реализует функцию getResults, которая считывает XML-файл в DOMDocument. Затем с помощью вызовов DOM выполняется обход всех тегов event и game с созданием массива событий. Внутри каждого элемента массива содержится хеш-таблица с названием события и массивом состоявшихся матчей. Эта структура, по существу, является хранимой в памяти версией структуры XML.

Для тестирования работы данного сценария мы создадим HTML-страницу экспорта, использующую функцию getResults для чтения файла, а затем выведем данные как серию HTML-таблиц. В листинге 3 приведен PHP-код для такого тестирования.

Листинг 3. HTML-страница результатов
<html><head><title>Event Results</title></head>
<body>
<?php
include_once('getresults.php');
$results = getResults();
foreach( $results as $event ) {
?>
<h1><?php echo( $event['name'] ) ?></h1>
<table>
<?php
foreach( $event['games'] as $game ) {
  $s1 = (int)$game['score1'];
  $s2 = (int)$game['score2'];
?>
<tr>
  <td style="font-weight:<?php echo( ( $s1 > $s2 ) ? 'bold' : 'normal') ?>">
    <?php echo( $game['team1'] ) ?></td>
  <td><?php echo( $s1 ) ?></td>
  <td style="font-weight:<?php echo( ( $s2 > $s1 ) ? 'bold' : 'normal') ?>">
    <?php echo( $game['team2'] ) ?></td>
  <td><?php echo( $s2 ) ?></td>
</tr>
<?php
}
?>
</table>
<?php
}
?>
</body></html>

Используя код getresults.php и XML-файл данных, загруженный на Web-сервер, можно увидеть результат в формате HTML, изображенный на рисунке 1.

Рисунок 1. Результат соревнований в HTML-формате
Рисунок 1. Результат соревнований в HTML-формате

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

Создание PDF

Теперь, имея данные, можно перейти к созданию PDF-файлов. Первый шаг – загрузка библиотеки FPDF и установка ее в том каталоге, где находится набор файлов приложения. На самом деле эту библиотеку можно установить в любом месте, если указать ее местоположение в переменной PHP library path. Запомните местоположение каталога font, поскольку нам понадобится установить 'FPDF_FONTPATH', как показано в листинге 4.

Листинг 4. Hello World в PDF-формате
<?php
define('FPDF_FONTPATH','/Library/WebServer/Documents/derby/font/');

require( 'fpdf.php' );

$pdf = new FPDF();
$pdf->SetFont('Arial','',72);
$pdf->AddPage();
$pdf->Cell(40,10,"Hello World!",15);
$pdf->Output();
?>

Этот сценарий выводит строку Hello World в PDF-формате. Прежде всего сценарий устанавливает местоположение каталога font FPDF, используя оператор define. Затем он определяет библиотеку FPDF, используя оператор require. Далее сценарий создает FPDF-объект, устанавливает шрифт, помещает заданный текст на страницу, используя метод Cell, и выводит ее в PDF-формате.

На рисунке 2 показан результат при правильной работе сценария.

Рисунок 2. Hello World в PDF-формате
Рисунок 2. Hello World в PDF-формате

Если PDF не выводится, придется, возможно, запустить сценарий из командной строки, чтобы увидеть сообщение об отсутствии файла fpdf.php или о возникновении каких-либо других проблем.

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

Листинг 5. Первая версия отображения результатов в PDF-формате
<?php
define('FPDF_FONTPATH','/Library/WebServer/Documents/derby/font/');

require( 'fpdf.php' );
require( 'getresults.php' );

class PDF extends FPDF
{
function EventTable($event)
{
    $this->Cell(40,10,$event['name'],15);
    $this->Ln();
}
}

$pdf = new PDF();
$pdf->SetFont('Arial','',48);
foreach( getResults() as $event ) {
  $pdf->AddPage();
  $pdf->EventTable($event);  
}
$pdf->Output();
?>

Вместо того чтобы использовать FPDF-класс извне, мы расширяем его собственным подклассом. В этом подклассе мы создаем новый метод под названием EventTable, который создает таблицу результатов для данного события. Начнем с малого и просто выведем имя события. Это имя выводится в расположенном в конце сценария цикле foreach, который добавляет страницу для каждого события, а затем активизирует метод EventTable.

Результаты работы сценария показаны на рисунке 3.

Рисунок 3. Первая версия динамического PDF
Рисунок 3. Первая версия динамического PDF

При прокрутке страниц видно, что каждое событие располагается на своей собственной странице. Следующий шаг – добавление результатов на страницу.


Создание таблицы результатов

В PDF-файлах нет такой простой табличной структуры, как в HTML. Для построения таблицы необходимо создать набор ячеек, имеющих различную ширину, шрифт, цвет фона, цвет линий и т.д.

В листинге 6 приведен дополнительный код, настраивающий строку заголовка для таблицы.

Листинг 6. Добавление заголовка таблицы результатов
<?php
define('FPDF_FONTPATH','/Library/WebServer/Documents/derby/font/');

require( 'fpdf.php' );
require( 'getresults.php' );

class PDF extends FPDF
{
function EventTable($event)
{
    $this->SetFont('','B','24');
    $this->Cell(40,10,$event['name'],15);
    $this->Ln();

    $this->SetXY( 10, 45 );

    $this->SetFont('','B','10');
    $this->SetFillColor(128,128,128);
    $this->SetTextColor(255);
    $this->SetDrawColor(92,92,92);
    $this->SetLineWidth(.3);

    $this->Cell(70,7,"Team 1",1,0,'C',true);
    $this->Cell(20,7,"Score 1",1,0,'C',true);
    $this->Cell(70,7,"Team 2",1,0,'C',true);
    $this->Cell(20,7,"Score 2",1,0,'C',true);
    $this->Ln();
}
}

$pdf = new PDF();
$pdf->SetFont('Arial','',10);
foreach( getResults() as $event ) {
  $pdf->AddPage();
  $pdf->EventTable($event);  
}
$pdf->Output();
?>

Этот дополнительный код настраивает шрифт, цвета и толщину линий. Затем он визуализирует несколько ячеек, содержащих заголовки четырех столбцов. Далее вызывается метод Ln, выполняющий возврат каретки для перехода на новую строку.

Результат выполнения данного сценария в браузере показан на рисунке 4.

Рисунок 4. Страница со строкой заголовков столбцов таблицы
Рисунок 4. Страница со строкой заголовков столбцов таблицы

На рисунке 4 заголовки отображаются в виде белого текста на сером фоне. Такой формат выделит их из данных, которые будут отображаться под заголовками. Для визуализации результатов соревнований добавьте код, приведенный в листинге 7.

Листинг 7. Добавление заполненной таблицы результатов
<?php
define('FPDF_FONTPATH','/Library/WebServer/Documents/derby/font/');

require( 'fpdf.php' );
require( 'getresults.php' );

class PDF extends FPDF
{
function EventTable($event)
{
    $this->SetFont('','B','24');
    $this->Cell(40,10,$event['name'],15);
    $this->Ln();

    $this->SetFont('','B','10');
    $this->SetFillColor(128,128,128);
    $this->SetTextColor(255);
    $this->SetDrawColor(92,92,92);
    $this->SetLineWidth(.3);

    $this->Cell(70,7,"Team 1",1,0,'C',true);
    $this->Cell(20,7,"Score 1",1,0,'C',true);
    $this->Cell(70,7,"Team 2",1,0,'C',true);
    $this->Cell(20,7,"Score 2",1,0,'C',true);
    $this->Ln();

    $this->SetFillColor(224,235,255);
    $this->SetTextColor(0);
    $this->SetFont('');

    $fill = false;

    foreach($event['games'] as $game)
    {
        $this->SetFont('Times',((int)$game['score1']>(int)$game['score2'])?'BI':'');
        $this->Cell(70,6,$game['team1'],'LR',0,'L',$fill);
        $this->Cell(20,6,$game['score1'],'LR',0,'R',$fill);
        $this->SetFont('Times',((int)$game['score1']<(int)$game['score2'])?'BI':'');
        $this->Cell(70,6,$game['team2'],'LR',0,'L',$fill);
        $this->Cell(20,6,$game['score2'],'LR',0,'R',$fill);
        $this->Ln();
        $fill =! $fill;
    }
    $this->Cell(180,0,'','T');
}
}

$pdf = new PDF();
$pdf->SetFont('Arial','',10);
foreach( getResults() as $event ) {
  $pdf->AddPage();
  $pdf->EventTable($event);  
}
$pdf->Output();
?>

В дополнение к выводу строки заголовков у нас есть цикл foreach в методе EventTable, который выполняет итерирование по всем матчам. На рисунке 5 показан код реализации.

Рисунок 5. PDF с таблицей результатов
Рисунок 5. PDF с таблицей результатов

Переменная $fill чередует цвета фона строк таблицы. Названия и результаты выигравших команд отображаются жирным курсивом. Также отмечу, что шрифт меняется с Arial для заголовков на Times для содержимого матчей.

Чтобы завершить код примера, нужно добавить немного графики.


Добавление графики

Добавить графику в PDF очень просто. Для начала найдите какое-либо изображение в Интернете. Я взял логотип одной из команд и сохранил его в формате PNG. Затем я применил новый код, приведенный в листинге 8.

Листинг 8. Добавление изображения логотипа
<?php
define('FPDF_FONTPATH','/Library/WebServer/Documents/derby/font/');

require( 'fpdf.php' );
require( 'getresults.php' );

class PDF extends FPDF
{
function EventTable($event)
{
    $this->Image('logo.png',5,5,33);

    $this->SetXY( 40, 15 );

    $this->SetFont('','B','24');
    $this->Cell(40,10,$event['name'],15);
    $this->Ln();

    $this->SetXY( 10, 45 );

    $this->SetFont('','B','10');
    $this->SetFillColor(128,128,128);
    $this->SetTextColor(255);
    $this->SetDrawColor(92,92,92);
    $this->SetLineWidth(.3);

    $this->Cell(70,7,"Team 1",1,0,'C',true);
    $this->Cell(20,7,"Score 1",1,0,'C',true);
    $this->Cell(70,7,"Team 2",1,0,'C',true);
    $this->Cell(20,7,"Score 2",1,0,'C',true);
    $this->Ln();

    $this->SetFillColor(224,235,255);
    $this->SetTextColor(0);
    $this->SetFont('');

    $fill = false;

    foreach($event['games'] as $game)
    {
      $this->SetFont('Times',((int)$game['score1']>(int)$game['score2'])?'BI':'');
      $this->Cell(70,6,$game['team1'],'LR',0,'L',$fill);
      $this->Cell(20,6,$game['score1'],'LR',0,'R',$fill);
      $this->SetFont('Times',((int)$game['score1']<(int)$game['score2'])?'BI':'');
      $this->Cell(70,6,$game['team2'],'LR',0,'L',$fill);
      $this->Cell(20,6,$game['score2'],'LR',0,'R',$fill);
      $this->Ln();
      $fill =! $fill;
    }
    $this->Cell(180,0,'','T');
}
}

$pdf = new PDF();
$pdf->SetFont('Arial','',10);
foreach( getResults() as $event ) {
  $pdf->AddPage();
  $pdf->EventTable($event);  
}
$pdf->Output();
?>

Основным в листинге 8 является метод Image, который принимает название файла с изображением, его местоположение и ширину. Все дополнительные параметры необязательны, поэтому указываем столько информации, сколько хотим.

Несколько новых вызовов SetXY перемещают текст и таблицу в подходящее место, чтобы они не перекрывали изображение.

На рисунке 6 показан результат работы данного сценария.

Рисунок 6. Готовый PDF с логотипами
Рисунок 6. Готовый PDF с логотипами

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


Заключение

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

Ресурсы

Научиться

Получить продукты и технологии

  • PDFLib: коммерческая библиотека для создания PDF-файлов.
  • PDFLib-Lite: загрузите версию библиотеки с открытыми исходными кодами PDFLib 7 и исследуйте ее функциональные возможности.
  • Пакет PECL: загрузите расширение, содержащее библиотеку программирования PDFLib, для обработки PDF-файлов на лету.
  • FPDF: загрузите библиотеку, при помощи которой создавались PDF-файлы данной статьи.
  • Ознакомительные версии продуктов IBM. Загрузите ознакомительные версии продуктов IBM или исследуйте в IBM SOA Sandbox интерактивные ознакомительные версии, чтобы освоить инструменты разработки приложений и продукты промежуточного уровня семейств DB2®, Lotus®, Rational®, Tivoli® и WebSphere®.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



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

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML, Open source
ArticleID=814631
ArticleTitle=Динамическое создание PDF-файлов при помощи PHP
publish-date=05112012