Создаем документ PDF из PHP скрипта с помощью библиотеки FPDF. Сравнение способов генерации pdf из html Дополнительная информация о создании pdf

Генерация pdf-документов является повседневной задачей в веб-разработке. В перечень таких документов входят счета, накладные, полисы и прочие. Существует множество готовых библиотек для решения этой задачи, в том числе и для php. Например, mpdf ,tcpdf , и многие другие. Файл можно собрать с помощью api этих библиотек, но это довольно долгое занятие. А времени на реализацию задачи много не бывает, не так ли? Поэтому чаще всего pdf-файл создается из html-представления, что довольно удобно. Но, к сожалению, не все так просто. У такого подхода есть множество подводных камней, способных вывести из себя кого угодно.

Например:

  • Стили нельзя подключить отдельно, следовательно они должны быть включены в html-документ отдельным блоком, либо инлайново для каждого элемента. В этом нет ничего страшного, небольшое неудобство.
  • К сожалению, в таких библиотеках некоторые стили могут работать не так как этого от них ожидаешь, либо не работать в принципе. Это самый главный недостаток.
  • Из предыдущего пункта следует, что создать документ максимально соответствующий требованиям очень трудоемко, а порой просто невозможно.
  • А в случае разработки под Битрикс есть еще одна проблемка. Все знают, что для работы платформы требуется в php.ini установить параметр mbstring.func_overload в значение 2. А для создания pdf-файла, содержащего кириллицу потребуется значение 0. Обычно эта проблема решается с помощью настройки веб-сервера, но все равно неприятно.

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

Пришло время перейти к главной части этой статьи. Помимо вариантов предложенных выше, существует альтернативный - PhantomJS .

PhantomJS - это сборка движка WebKit без графического интерфейса, позволяющая в режиме консоли загружать веб-страницу, выполнять JavaScript, полноценно работать с DOM, Canvas и SVG.

Конечно, помимо перечисленных выше возможностей, он дает возможность создавать pdf-файлы.

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

Процесс установки PhantomJS достаточно подробно описан в документации , поэтому я не буду останавливаться на этом вопросе.

Важным моментом в работе PhantomJS является js-файл (далее config.js), который своим содержанием определяет то, что именно мы хотим сделать. На официальном сайте есть множество готовых примеров таких файлов, с помощью которых решаются самые разные задачи, например, unit-тестирование. В числе примеров есть сохранение веб-страницы в формате pdf . Это именно то, что нам нужно.

Пара небольших правок позволит использовать этот пример в наших целях.

var page = require ("webpage" ). create (), system = require ("system" ), address , output , size ; //если аргументов мало или слишком много, выводится сообщение с помощью по использованию if (system . args . length < 3 || system . args . length > 5 ) { console . log ("Usage: config.js URL filename " ); console . log (" paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"" ); console . log (" image (png/jpg output) examples: "1920px" entire page, window width 1920px" ); console . log (" "800px*600px" window, clipped to 800x600" ); phantom . exit (1 ); } else { // обработка аргументов address = system . args [ 1 ]; output = system . args [ 2 ]; page . viewportSize = { width : 800 , height : 800 }; if (system . args . length > 3 && system . args [ 2 ]. substr (- 4 ) === ".pdf" ) { size = system . args [ 3 ]. split ("*" ); page . paperSize = (size . length === 2 ) ? { width : size [ 0 ], height : size [ 1 ], margin : "0px" } : { format : system . args [ 3 ], orientation : "portrait" , margin : "1cm" }; } else { console . log ("Invalid path to pdf!" ); phantom . exit (1 ); } if (system . args . length > 4 ) { page . zoomFactor = system . args [ 4 ]; } // открытие страницы и сохранение результата page . open (address , function (status ) { if (status !== "success" ) { console . log ("Unable to load the address!" ); phantom . exit (1 ); } else { window . setTimeout (function () { page . render (output ); phantom . exit (); }, 200 ); } }); }

$ phantomjs path/to/config.js "url" path/to/pdf/file "A4"

Разберем аргументы по порядку:

  1. Путь до config.js.
  2. Адрес веб-страницы, которую надо преобразовать в pfd-файл.
  3. Путь до pdf-файла, в который произойдет сохранение результата.
  4. Формат pdf-файла.

Получаем искомый pdf-файл.

Возвращаясь к php, это решение довольно просто интегрировать в код.

В простейшем случае это выглядит вот так:

$command = sprintf( "phantomjs %s %s %s %s", $fullPathToConfigJS, $url, $fullPathToSave, $format ); exec($command);

Итак, подведу итоги. На мой взгляд, такое решение имеет следующие достоинства:

  • Сверстать макет для такого документа намного проще. Это очень важно, т.к. не только упрощает разработку, но и не превращает в кошмар последующие правки документа.
  • Избавляет от нужды извращаться с mbstring.func_overload. Но это, несомненно, проблема характерная в основном для Битрикс.

Конечно, есть и недостатки:

  • Не 100% поддержка всех css-стилей. Но в сравнении с библиотеками, перечисленными в начале стати, все очень хорошо.
  • Такое решение может отпугнуть начинающего разработчика.
03.12.2015

PDF (portable document format) - это популярный формат документов, который не зависит от операционной системы или железа. Очевидно, его можно использовать для генерации отчетов, дипломов, чеков в приложениях, а так же для редактирования документов. Для этих целей лучше всего подойдет java-библиотека iText . Кстати, библиотека доступна так же и для C#.

В статье мы рассмотрим следующие вопросы:

  • Генерация нового PDF документа на примере чека.
  • Заполнение шаблона PDF документа.
  • Краткий обзор некоторых классов и методов библиотеки iText.

Для нетерпеливых сразу же привожу java код, который создает pdf-шаблон чека, а потом заполняет его данными из объекта Receipt. Не забудьте скачать jar-файл библиотеки и положить его на classpath.

Import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import java.io.FileInputStream; import java.io.FileOutputStream; import java.net.URL; import java.text.NumberFormat; import java.util.Date; public class PdfGeneration { static int FONT_SIZE_SMALL = 16; static int FONT_SIZE_BIG = 32; static int OFFSET = 40; public static void main(String args) throws Exception { createTemplate(); Receipt receipt = new Receipt("This is a veeeeeeeeeeeeeeeeeeeeee" + "eeeeeeeeeeeeeeeeeeeeery long purpose " + "text, so it will overflow with font size = 16", 123.45, "Name Surname"); fillInReceipt(receipt); } public static void createTemplate() throws Exception { Document document = new Document(); Font font1 = new Font(Font.FontFamily.HELVETICA, FONT_SIZE_BIG, Font.BOLD); Font font2 = new Font(Font.FontFamily.HELVETICA, FONT_SIZE_SMALL, Font.ITALIC | Font.UNDERLINE); PdfWriter.getInstance(document, new FileOutputStream("template.pdf")); document.open(); // отцентрированный параграф Paragraph title = new Paragraph("Receipt", font1); title.setAlignment(Element.ALIGN_CENTER); title.setSpacingAfter(FONT_SIZE_BIG); document.add(title); // параграф с текстом Paragraph purpose = new Paragraph("Purpose", font2); purpose.setSpacingAfter(FONT_SIZE_BIG); document.add(purpose); // параграф с добавленным чанком текста Paragraph amount = new Paragraph(); amount.setFont(font2); amount.setSpacingAfter(8); amount.add(new Chunk("Amount")); document.add(amount); // параграф с фразой, в которую добавлен чанк Paragraph date = new Paragraph(); date.setFont(font2); Phrase phrase = new Phrase(); phrase.add(new Chunk("Date")); date.add(phrase); document.add(date); document.add(new Paragraph("Name", font2)); Paragraph footer = new Paragraph("Important - please retain for your records - "); // ссылка Anchor anchor = new Anchor("Javenue"); anchor.setReference("http://www..add(anchor); footer.setAlignment(Element.ALIGN_CENTER); footer.setSpacingBefore(FONT_SIZE_BIG); document.add(footer); // картинка, загруженная по URL String imageUrl = "http://www..png"; // Image.getInstance("sample.png") Image stamp = Image.getInstance(new URL(imageUrl)); stamp.setAlignment(Element.ALIGN_RIGHT); document.add(stamp); document.close(); } public static void fillInReceipt(Receipt receipt) throws Exception { PdfReader reader = new PdfReader(new FileInputStream("template.pdf")); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("receipt.pdf")); PdfContentByte stream = stamper.getOverContent(1); stream.beginText(); stream.setColorFill(BaseColor.BLUE); BaseFont font = BaseFont.createFont(); float pageWidth = reader.getPageSize(1).getWidth(); stream.setFontAndSize(font, FONT_SIZE_SMALL); float v = stream.getEffectiveStringWidth(receipt.getPurpose(), false); float fitSize = (pageWidth-OFFSET*2) * FONT_SIZE_SMALL/v; stream.setFontAndSize(font, fitSize); stream.setTextMatrix(OFFSET, 680); stream.showText(receipt.getPurpose()); stream.setFontAndSize(font, FONT_SIZE_SMALL); String amount = NumberFormat.getCurrencyInstance() .format(receipt.getAmount()); v = stream.getEffectiveStringWidth(amount, false); stream.setTextMatrix(pageWidth - v - OFFSET, 655); stream.showText(amount); v = stream.getEffectiveStringWidth(receipt.getDate() + "", false); stream.setTextMatrix(pageWidth - v - OFFSET, 630); stream.showText(receipt.getDate() + ""); v = stream.getEffectiveStringWidth(receipt.getName(), false); stream.setTextMatrix(pageWidth - v - OFFSET, 605); stream.showText(receipt.getName()); stream.endText(); stamper.setFullCompression(); stamper.close(); } static class Receipt { private String purpose; private double amount; private Date date = new Date(); private String name; public Receipt(String purpose, double amount, String name) { this.purpose = purpose; this.amount = amount; this.name = name; } public String getPurpose() { return purpose;} public void setPurpose(String purpose) { this.purpose = purpose; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getName() { return name; } public void setName(String name) { this.name = name; } } }

Для начала необходимо создать экземпляр com.itextpdf.text.Document и с помощью PDFWriter связать его с файлом на файловой системе. Создать шрифт можно с помощью класса Font, указывая размер шрифта, его семейство и стиль.

Основные объекты, с которыми вы будете работать, это Phrase, Paragraph, возможно Anchor и самый маленький кусок текста - Chunk. Обратите внимание, что для каждого параграфа вы можете задать свой шрифт и далее добавлять в параграф объекты Chunk, Phrase, Anchor.

C помощью следующего кода можно добавить картинку в PDF документ:

String imageUrl = "http://site.com/image.png"; Image stamp = Image.getInstance(new URL(imageUrl)); stamp.setAlignment(Element.ALIGN_RIGHT); document.add(stamp);

Не забудьте вызвать document.close() после добавления всех объектов. Это необходимо для того, чтобы корректно закрыть PDFWriter.

Писать поверх существующего PDF документа можно через PdfContentByte. Немного придется помучиться с расположением текста на чеке, но кто говорил, что будет легко. Думаю, код метода fillInReceipt понятен и не требует дополнительных объяснений.

А вот еще классы из iText API, которые могут вам пригодиться:

  • List и ListItem - это список и элемент списка, который может быть добавлен как list.add(new ListItem("item")). Списки как и в HTML могут быть упорядоченные и неупорядоченные.
  • Chapter и Section - раздел и соответственно его секция. Секции можно добавлять с помощью вызова chapter.addSection
  • Более сложный объект PdfPTable, состоящий из ячеек PdfPCell. Можно делать достаточно красивые таблички, но как по мне API не самый удобный.

Вот и все. Если есть вопросы - обращайтесь: всегда рад помочь.

PDF24 Creator, это бесплатный PDF генератор для вашего ПК. Генерация PDF файлов невероятно простая задача с этой программой. Комбинация дополнительных возможностей вместе с созданием PDF файлов, делает этот инструмент одной из самых признанных бесплатных программ. В качестве альтернативы вы также можете генерировать PDF онлайн. Прочтите ниже как это работает.

PDF24 Creator – бесплатный PDF генератор

PDF24 Creator, это бесплатный PDF генератор для Windows. Генерация PDF файлов реализована с помощью PDF принтера. После установки в вашей Windows появится новый виртуальный PDF принтер. Просто печатайте через этот принтер все, что хотите превратить в PDF. PDF принтер, это единственный путь создания PDF. Программа включает в себя широкий диапазон возможностей, которые помогут вам в генерации PDF. Эта программа бесплатна. Вы можете скачать свежую версию с этого сайта.

PDF24 Онлайн PDF Генератор – Генерация PDF файлов онлайн

Онлайн PDF Генератор, это полезный инструмент, если вы хотите преобразовать документы в PDF формат. Просто выберите документ или отправьте документ по почте в Онлайн PDF Генератор и через несколько секунд ваш PDF файл будет доступен для скачивания или отправлен вам обратно.

Email интерфейс для этого PDF генератора также доступен. Нужно отправить файлы генератору и дождаться ответного письма, в котором будут файлы, сконвертированные в PDF

Множество других PDF генераторов в инструментарии PDF24

Онлайн утилиты от PDF24 решают многие проблемы, связанные с PDF, достаточно просто, быстро и бесплатно. Многие из этих инструментов генерируют PDF файлы. Вы ещё не знакомы с онлайн утилитами PDF24? Взгляните, вы сможете использовать все эти инструменты.

Онлайн или офлайн?

Создавайте файлы PDF из любого приложения. Работайте в режиме онлайн, используя наше онлайн-приложение, либо в режиме офлайн, загрузив настольное приложение Soda PDF на свой компьютер.

Создайте файл

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

Отправьте по почте

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

ЗНАЕТЕ ЛИ ВЫ?

Есть новый, экологичный способ печати документов!

Нет необходимости использовать бумагу, когда вы можете использовать виртуальную печать. Когда вы загрузите приложение Soda PDF для настольного ПК, оно установит виртуальный принтер Soda PDF на ваш компьютер. Затем вы можете выбрать принтер Soda PDF из любой программы, чтобы преобразовать любой документ в формат PDF, не открывая настольное приложение Soda PDF! Когда вы будете готовы напечатать свой документ, просто выберите принтер Soda PDF из меню принтеров, и тогда будет создана копия документа в формате PDF. С помощью Soda PDF можно создавать файлы PDF из файлов более чем 300 форматов.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ О СОЗДАНИИ PDF

Формат PDF

Формат PDF был создан компанией Adobe Systems в 1993 году. В 2008 году он был стандартизован в качестве открытого формата, развитием которого стал заниматься независимый комитет ISO.

PDF 2.0

Формат PDF значительно эволюционировал с момента своего создания. Самой последней версией является версия PDF 2.0, стандарт которой был опубликован 28 июля 2017 г.

Другие возможности

Если вы хотите создать файл PDF путем объединения нескольких файлов, используйте наш бесплатный онлайн-инструмент "Объединить PDF". Вы даже можете выбрать порядок следования отдельных файлов в общем файле.

Где это может пригодиться? При необходимости генерации готовых к печати файлов в web-приложении по уже имеющемуся произвольному жесткому шаблону: сертификаты, бейджи, пропуски и прочее.

Почему PDF? Формат PDF позволяет создавать документы с целым рядом неоспоримых преимуществ: открытость, кроссплатформеность, распространенность и, что очень важно, точностью и неизменностью передачи данных по цепочке создание, просмотр и печать.

В чем соль? В использовании SVG файлов как шаблонов с возможностью подстановки необходимых полей с последующим преобразованием в PDF.

Какие преимущества? Возможность создания и быстрого редактирования очень сложных шаблонов в привычных векторных редакторах, таких как Adobe Illustrator, Corel Draw или Inkscape. Простота программирования и использование только бесплатных программных средств. Еще одним важным преимуществом является возможность прозрачно использовать UTF-8 для вставляемых текстов.

Что для этого надо? Для использования данного метода нужен выделенный сервер с возможностью установки своих приложений (Inkscape и GhostScript) и выполнением system-команд. При этом всё будет работать как на Windows платформе, так и на Linux.

Думаю, краткий FAQ осветил основные вопросы по данному методу, потому сразу приступим к разбору его сути.

Как известно, формат векторной графики SVG фактически представляет собой XML-файл, поэтому уже созданный файл достаточно просто редактировать простейшими средствами программирования. В случае использования SVG файла как жесткого шаблона, процесс упрощается в разы, т.к. нам нет необходимости менять структуру документа, а нужно лишь произвести подстановку нужных текстовых значений или кодированных в base64 растровых изображений.

Создать первоначальный шаблон можно в любом векторном редакторе поддерживающем экспорт в svg: Adobe Illustrator, Corel Draw или в самом Inkscape’е. Использование последнего желательно, хотя бы на последней, доводочной стадии, так как, в конечном счете, именно ему и предстоит производить необходимое нам преобразование.

При использовании растра в шаблоне, можно использовать 2-а метода, хранить растр в отдельном внешнем файле или встроенным в сам SVG файл. При необходимости менять в шаблоне растровый рисунок в первом случае можно перед генерацией менять и файл. При хранении рисунка встроенным в файл, следует в свойстве URL объекта рисунка прописать строку:

где {IMAGE} - это поле для вставки шаблонизатором base64 кодированного изображения.

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


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

Думаю, вы уже обратили внимание на то, что в местах предполагаемого текста вставлены тэги шаблонизатора (в данном примере использовался ). Именно использование XML совместимых тегов дает возможность прописывать их в самом векторном редакторе не прибегая к дополнительному редактированию.

Мы имеем шаблон, и уже без проблем сможем вставить необходимые нам данные, но как собственно мы будем проводить преобразование? Для этого воспользуемся интерфейсом командной строки Inkscape:

#преобразование в PDF-файл
inkscape -A

Используя ключ «-A » мы сразу получим PDF файл, но, к сожалению, создаваемый напрямик PDF имеет очень большие размеры. Для решения этой проблемы можно пойти в обход. А именно, использовать экспорт SVG не на прямую в PDF, а по цепочке SVG->PS->PDF . Использовав для конечного формирования PDF файла утилиту ps2pdf из комплекта Ghost Script, мы можем уменьшить размеры финального файла в десятки раз.
#преобразование в PostScript-файл
inkscape -P
#преобразование в PostScript-файла в PDF
ps2pdf

Единственный минус в том, что в этом случае мы потеряем все эффекты прозрачности, так как формат PostScript его не поддерживает.

Для полной переносимости сгенерированных документов, можно добавить Inscape’у опцию «-T » преобразования всего текста в кривые. Этим самым мы сможем избавиться от проблем наличием шрифтов на клиентской машине, а также от проблем с кодировками.

Теперь мы имеем, всё необходимое: SVG шаблон и команды преобразований. Напишем php-скрипт, который бы выдавал pdf-файл сгенерированный из шаблона.

/* ****************************************************************************************
* Скрипт формирования pdf-файла пропуска с помощью последовательного преобразования
* шаблона в svg файл, после чего тот преобразуется в PostScript файл программой Inkscape,
* и последний преобразуется в pdf с помощью утилиты ps2pdf.
*
* Автор: Шебастюк В.В. a.k.a. JStingo
* **************************************************************************************** */

/* параметры скрипта */

//Путь к папке с временными файлами
//(если не указан, то файлы будут хранится в системной временной папке)
$tmp_dir = "" ;//генерируем пути к временным svg, ps и pdf файлам
$tmp_svg_file = tempnam ($tmp_dir , "" );
$tmp_ps_file = tempnam ($tmp_dir , "" );
$tmp_pdf_file = tempnam ($tmp_dir , "" );/* Шаблонизатор FastTemplate */
include(«include/cls_fast_template.php» );
$tpl = new FastTemplate («templates» );try {/* Блок с получаемыми для шаблонизации данными */
/* ........................... */
$user_name = "JStingo" ;
$register_date = "28/09/2007" ;
/* ........................... */
/* формируем имя результрующего файла в виде User_name.pdf */
$pdf_file_name = $user_name . ".pdf" ;/* обработка шаблона и получение результрующего файла */$tpl -> define (array("svg" => «template.svg» ));
$tpl -> assign (array("USER_NAME" => $user_name ,
"R_DATE" => $register_date
));
$tpl -> parse ("SVG" , "svg" );//сохраням полученный svg файл
$tpl -> FastWrite ("SVG" , $tmp_svg_file );//производим конвертацию svg-файла средствами inkscape"а в ps-файл
//Ключи
// -T - служит для преобразования текста в кривые (для нормальной поддержки шрифтов)
// -P - указывает на необходимость преобразования в PostScript-файл
system («inkscape -T $tmp_svg_file -P $tmp_ps_file» , $success );if($success != 0 )
throw new Exception («Ошибка формирования ps-файла.» );//преобразуем ps-файл в pdf с помощью утилиты ps2pdf

//Ключи
// -dUseFlateCompression=true - устанавливает использование компрессии
// -dPDFSETTINGS=/printer - устанавливает оптимизацию для печати

system («ps2pdf -dUseFlateCompression=true -dPDFSETTINGS=/printer $tmp_ps_file $tmp_pdf_file» , $success );//в случае неудачного выполнения преобразования формируем исключение
if($success != 0 )
throw new Exception («Ошибка формирования pdf-файла.» );//заголовок о том, что будем оправлять pdf-файл
header ("Content-type: application/pdf" );// Называться будет как $pdf_file_name
header ("Content-Disposition: attachment; filename="" . $pdf_file_name . """ );// передаем сгенерированный файл
readfile ($tmp_pdf_file );//удаляем временные файлы
@ unlink ($tmp_svg );
@ unlink ($tmp_ps_file );
@ unlink ($tmp_pdf_file );catch (Exception $e ){
/* Если где-то произошла ошибка, то сообщаем об этом */
$tpl -> define (array("error" => «error.tpl» ));
$tpl -> assign ("ERROR" , $e -> getMessage ());$tpl -> parse ("ERROR" , "error" );
$tpl -> FastPrint ("ERROR" );
}
?>

Думаю, всем не составит особого труда переписать скрипт на другом языке программирования.