Sample documents are in the "examples" directory. "index.php" file is the web interface to browse examples, "cli.php" is a console interface. Via the web interface, the documents are available in pdf and jpeg format (the jpeg format requires Imagick).
PHPPdf is library that transforms an XML document to a PDF document or graphics files. The XML source document is similar to HTML, but there are lots of differences in names and properties of attributes, properties of tags, and there are a lot of not standard tags, not all tags from html are supported, stylesheet is described in an xml document, not in css.
Assumption of this library is not HTML -> PDF / JPEG / PNG, but XML -> PDF / JPEG / PNG transformation. Some tags and attributes are the same as in HTML in order decrease the learning curve of this library.
PHPPdf is available at packagist.org, so you can use composer to download this library and all dependencies.
(add to require section in your composer.json file)
"psliwa/php-pdf": "*"
You should choose last stable version (or wildcard of stable version), wildcard char ("*") is only an example.
If you want to use as features as barcodes or image generation, you should add extra dependencies:
"zendframework/zend-barcode": ">=2.0.0,<2.4",
"zendframework/zend-validator": ">=2.0.0,<2.4",
"imagine/Imagine": ">=0.2.0,<0.6.0"
There is a Symfony2 bundle which integrates this library with the Symfony2 framework.
Diacritical marks are not displayed, what should I do?
You should set a font that supports the encoding that you are using, and set this encoding as "encoding" attribute for "page" and/or "dynamic-page" tags. PHPPdf provides some free fonts that support utf-8 encoding, for example, DejaVuSans. The "Font" example shows how to change the font type by using a stylesheet.
You can also add custom fonts, in order that you should prepare xml config file and configure Facade object as shown below:
<!-- xml config file code -->
<fonts>
<font name="DejaVuSans">
<normal src="https://github.com/psliwa/PHPPdf/raw/master/%resources%/fonts/DejaVuSans/normal.ttf" /><!-- "%resources%" will be replaced by path to PHPPdf/Resources directory -->
<bold src="https://github.com/psliwa/PHPPdf/raw/master/%resources%/fonts/DejaVuSans/bold.ttf" />
<italic src="https://github.com/psliwa/PHPPdf/raw/master/%resources%/fonts/DejaVuSans/oblique.ttf" />
<bold-italic src="https://github.com/psliwa/PHPPdf/raw/master/%resources%/fonts/DejaVuSans/bold+oblique.ttf" />
<light src="https://github.com/psliwa/PHPPdf/raw/master/%resources%/fonts/DejaVuSans/light.ttf" />
<light-italic src="https://github.com/psliwa/PHPPdf/raw/master/%resources%/fonts/DejaVuSans/light+oblique.ttf" />
</font>
</fonts>
//php code
$loader = new PHPPdf\Core\Configuration\LoaderImpl();
$loader->setFontFile(/* path to fonts configuration file */);
$builder = PHPPdf\Core\FacadeBuilder::create($loader);
$facade = $builder->build();
<!-- xml document code -->
<pdf>
<dynamic-page encoding="UTF-8" font-type="DejaVuSans">
</dynamic-page>
</pdf>
You can find more datails in the Configuration section.
Generating of a simple pdf file with png images takes a lot of time and memory, what should I do?
PHPPdf uses the Zend_Pdf library that poorly supports png files without compression. You should compress the png files.
How can I change the page size/orientation?
To set the page dimensions you use the "page-size" attribute of the page or dynamic-page tags.
The value syntax of this attribute is "width:height".
There are however standard predefined values:
All formats are supported in portrait and lanscape.
Example:
<page page-size="100:50">text</page>
<page page-size="a4">text</page>
<page page-size="letter-landscape">text</page>
The simplest way of using the library is:
//register the PHPPdf and vendor (Zend_Pdf and other dependencies) autoloaders
require_once 'PHPPdf/Autoloader.php';
PHPPdf\Autoloader::register();
PHPPdf\Autoloader::register('/path/to/library/lib/vendor/Zend/library');
//if you want to generate graphic files
PHPPdf\Autoloader::register('sciezka/do/biblioteki/lib/vendor/Imagine/lib');
$facade = new PHPPdf\Core\Facade(new PHPPdf\Core\Configuration\Loader());
//$documentXml and $stylesheetXml are strings contains XML documents, $stylesheetXml is optional
$content = $facade->render($documentXml, $stylesheetXml);
header('Content-Type: application/pdf');
echo $content;
The library bases pages on an XML format similar to HTML, but this format isn't HTML - some tags are diffrent, interpretation of some attributes is not the as same as in the HTML and CSS standards, adding attributes is also different.
A simple document has following structure:
<pdf>
<dynamic-page>
<h1>Header</h1>
<p>paragraph</p>
<div color="red">Layer</div>
<table>
<tr>
<td>Column</td>
<td>Column</td>
</tr>
</table>
</dynamic-page>
</pdf>
Adding a DOCTYPE declaration is strongly recommended in order to replace html entities on values:
<!DOCTYPE pdf SYSTEM "%resources%/dtd/doctype.dtd">
The root name of a document must be "pdf". The "dynamic-page" tag is an auto breakable page. The "page" tag is an alternative, and represents only a single, no breakable page.
The way of attribute setting is different than in HTML.
In order to set a background and border you need to use complex attributes, where first part of attribute name is a complex attribute type, and the second part is the property of this attribute.
Complex attribute parts are separated by a dot (".").
An another way of setting complex attributes is by using the "complex-attribute" tag.
Example:
<pdf>
<dynamic-page>
<div color="red" border.color="black" background.color="pink">
This text is red on pink backgroun into black border
</div>
</dynamic-page>
</pdf>
Alternative syntax ("stylesheet" tag):
<pdf>
<dynamic-page>
<div>
<stylesheet>
<attribute color="red" />
<complex-attribute name="border" color="black" />
<complex-attribute name="background" color="pink" />
</stylesheet>
This text is red on pink backgroun into black border
</div>
</dynamic-page>
</pdf>
Attributes can by set as XML attributes, directly after a tag name or by using the mentioned "stylesheet" tag. The HTML "style" attribute does not exist the PHPPdf XML dialect.
The library is very strict in respecting the corectness of tags and attributes. If an unexisted tag or attribute is detected, the document parser will stop and throw an exception.
The "id" attribute has an different usage than in HTML. The id attribute is used to identify tags when using inheritance.
The "name" attribute can also be used as an alias to "id".
An id must by unique throughout the document, otherwise a parsing error is thrown.
Example:
<pdf>
<dynamic-page>
<div id="layer-1" color="red" font-type="judson" font-size="16px">
<stylesheet>
<complex-attribute name="border" color="green" />
</stylesheet>
Layer 1
</div>
<div extends="layer-1">
Layer 2 inherits style (type, simple and complex attributes) from layer 1)
</div>
</dynamic-page>
</pdf>
The Second layer inherits all attributes (simple and complex), and also those from external stylesheets.
Priorites in attributes setting:
Example:
<pdf>
<page>
<div id="1" color="#cccccc" height="100px" text-align="right">
</div>
<div extends="1" color="#aaaaaa" height="150px">
<stylesheet>
<attribute name="height" value="200px" />
</stylesheet>
</div>
</page>
</pdf>
The second "div" will now have the following attributes:
Stylesheets are defined in external files, stylesheet short and long declarations of attributes are supported.
Syntax of the stylesheet:
Short style:
<stylesheet>
<!-- style attributes are embeded as xml attributes, class attribute has the same meaning as in HTML/CSS -->
<div class="class" font-size="12px" color="gray" background.color="yellow">
<!-- nested element, equivalent CSS selector syntax: "div.class p" -->
<p margin="10px 15px">
</p>
</div>
<!-- equivalent CSS selector syntax: ".another-class", "any" tag is wildcard (mean any tag) -->
<any class="another-class" text-align="right">
</any>
<h2 class="header">
<span font-size="9px">
</span>
<div font-style="bold">
</div>
</h2>
</stylesheet>
Long style:
<stylesheet>
<div class="class">
<!-- simple and complex attributes are nested in "div.class" selector path -->
<attribute name="font-size" value="12px" />
<attribute name="color" value="grey" />
<!-- equivalent of background.color attribute -->
<complex-attribute name="background" color="yellow" />
<!-- another nested element, equivalent CSS selector syntax: "div.class p" -->
<p>
<attribute name="margin" value="10px 15px" />
</p>
</div>
<!-- equivalent CSS selector syntax: ".another-class", "any" tag is wildcard (mean any tag) -->
<any class="another-class">
<attribute name="text-align" value="right" />
</any>
<h2 class="header">
<span>
<attribute name="font-size" value="9px" />
</span>
<div>
<attribute name="font-style" value="bold" />
</div>
</h2>
</stylesheet>
PHPPdf supports color palettes, - mapping of logical names to real colors.
Color palettes gives you the opportunity to create or overwrite default named colors. By default, PHPPdf supports named colors from the W3C standard (for example "black" = "#000000").
You can use a palette for the DRY principle, because information about used colors will be kept in one place. And you can also generate one document with different palettes.
Example:
<!-- colors.xml file -->
<colors>
<color name="header-color" hex="#333333" />
<color name="line-color" hex="#eeeeee" />
</colors>
<!-- stylesheet.xml file -->
<h2 color="header-color" />
<hr background-color="line-color" />
<table>
<td border-color="line-color" />
</table>
<!-- document.xml file -->
<pdf>
<page>
<h2>Header</h2>
<hr />
<table>
<tr>
<td>Data</td>
<td>Data</td>
</tr>
</table>
</page>
</pdf>
//php code
use PHPPdf\DataSource\DataSource;
$facade = ...;
$content = $facade->render(
DataSource::fromFile(__DIR__.'/document.xml'),
DataSource::fromFile(__DIR__.'/stylesheet.xml'),
DataSource::fromFile(__DIR__.'/colors.xml')
);
The library supports primary HTML tags: div, p, table, tr, td, b, strong, span, a, h1, h2, h3, h4, h5, img, br, ul, li
In addition there are also not standard tags:
There are tags that are only bags for attributes, a set of tags etc:
Complex attributes can be set by notation "attributeName.attributeProperty" or "attributeName-attributeProperty"
For example: border.color="black"
or border-color="black"
border:
background:
It is possible to add several complex attributes in the same type (for instance 3 different borders).
You can achieve that by using the "stylesheet" tag instead of the short notation.
<pdf>
<dynamic-page>
<div>
<stylesheet>
<!-- Top and bootom edges are red, side edges are yellow-gray -->
<complex-attribute name="border" color="red" type="top+bottom" />
<complex-attribute id="borderLeftAndRight" name="border" color="yellow" type="left+right" size="4px" />
<complex-attribute id="outerBorderLeftAndRight" name="border" color="gray" type="left+right" size="2px" position="1px" />
</stylesheet>
</div>
</dynamic-page>
</pdf>
In this example, the second border has a "borderLeftAndRight" indentifie, if this border had no id, the attributes from second border would be merged with the attributes from first border.
Remeber the default identifier "id" is as same as the "name" attribute. The "id" attributes for complex attributes has nothing to do with the "id" attribute of tags (used in inheritance).
It is possible to create complex borders the same as in the previous example (outerBorderLeftAndRight).
Supported units for numerical attributes:
Currently unsupported units are: em and ex
When the unit is missing (for example: font-size="10"), then the default unit is points (pt). 1pt = 1/72 inch
Barcodes are supported by the <barcode>
tag.
PHPPdf uses the Zend\Barcode library in order to generate barcodes.
Example:
<pdf>
<dynamic-page>
<barcode type="code128" code="PHPPdf" />
</dynamic-page>
</pdf>
<barcode>
tag supports the most of standard attributes and has some other attributes:
You can find the description of these options and there default values in the Zend\Barcode documentation.
In order to render textual barcodes, you can't use to following embeded pdf fonts: courier, times-roman and helvetica. This will soon be fixed.
PHPPdf supports drawing simple charts.
For now there is only support for s simple pie chart.
Example:
<pdf>
<dynamic-page>
<pie-chart radius="200px" chart-values="10|20|30|40" chart-colors="black|red|green|blue"></pie-chart>
</dynamic-page>
</pdf>
The <pie-chart>
tag has three extra attributes:
The library supports external and internal hyperlinks.
External hyperlinks link to url's, while internal links link to other tags inside the pdf document.
Example:
<pdf>
<dynamic-page>
<a href="http://google.com">go to google.com</a>
<br />
<a ref="some-id">go to another tag</a>
<a href="#some-id">go to another tag</a> <!-- anchor style ref -->
<page-break />
<p id="some-id">Yep, this is another tag! ;)</p>
</dynamic-page>
</pdf>
Every element has a "href" and "ref" attribute, even div. You can't nest elements inside an "a" tag.
If you want to use img elements as a link, you should use the href (external link) or ref (internal link) attribute directly in img tag.
The preferred way of creating bookmarks is by using the "behaviours" tag.
This doesn't restrict the structure of the document, the owner of a parent bookmark doesn't have to be a parent of a child's bookmark owner.
Example:
<pdf>
<dynamic-page>
<div>
<behaviours>
<bookmark id="1">parent bookmark</bookmark>
</behaviours>
Some content
</div>
<div>
<behaviours>
<bookmark parentId="1">children bookmark</bookmark>
</behaviours>
Some another content
</div>
<div>
<behaviours>
<bookmark parentId="1">another children bookmark</bookmark>
</behaviours>
Some another content
</div>
<div>
<behaviours>
<bookmark>another parent bookmark</bookmark>
</behaviours>
Some content
</div>
</dynamic-page>
</pdf>
A shortcut for the "bookmark" behaviour is the "bookmark" attribute, if you assign some value to this attribute, bookmarks that refers to this tag will be automatically created.
The bookmark of a parent tag is also the parent of a children's bookmarks.
Example:
<pdf>
<dynamic-page>
<div bookmark="parent bookmark">
Some content
<div bookmark="children bookmark">
Some another content
</div>
<div bookmark="another children bookmark">
Some another content
</div>
</div>
<div bookmark="another parent bookmark">
Some content
</div>
</dynamic-page>
</pdf>
The above structures (both examples) will create this bookmarks structure:
Sticky notes can be created by using the "note" attribute.
Example:
<pdf>
<dynamic-page>
<div note="note text"></div>
</dynamic-page>
</pdf>
The XML parser normalizes values of attributes, wich results ignoring new line characters.
If you want to add a note with new line characters, you should use this syntax:
<pdf>
<dynamic-page>
<div>
<behaviours>
<note>note text</note>
</behaviours>
</div>
</dynamic-page>
</pdf>
"placeholders" can be used in for adding a repetitive header or/and footer.
Some elements have special "placeholders": page has header and footer, table also has header and footer (TODO: not implemented yet) etc.
<pdf>
<dynamic-page>
<placeholders>
<header>
<div height="50px" width="100%">
Header
</div>
</header>
<footer>
<div height="50px" width="100%">
Footer
</div>
</footer>
</placeholders>
</dynamic-page>
</pdf>
Header and footer need to have a height attribute set. This height is pooled with page top and bottom margins.
Workspace is the page size reduced by page margins and placeholders (footer and header) height.
Page can have a "watermark" placeholder.
The watermark may be set on block's and container elements, for instance: div, p, h1 (no span, plain text or img).
If you want to use an image as a watermark, you should wrap the img tag in a div.
Example:
<pdf>
<dynamic-page>
<placeholders>
<watermark>
<!-- rotate can have absolute values (45deg - in degrees, 0.123 - in radians) or relative values ("diagonally" and "-diagonally" - angle between diagonal and base side of the page) -->
<div rotate="diagonally" alpha="0.1">
<img src="https://github.com/psliwa/PHPPdf/raw/master/path/to/image.png" />
</div>
</watermark>
</placeholders>
</dynamic-page>
</pdf>
There are two tags that can be used to show page information in a footer, header or watermark: page-info and page-number.
This element only works with dynamic-page, not single pages. Page-info shows the current and total page number, page-number shows only the current page number.
Attributes of this tags:
Example:
<pdf>
<dynamic-page>
<placeholders>
<header>
<div height="20px">
<page-info format="page %s for %s" />
<!-- when we would like to number from 2 -->
<page-info offset="1" format="page %s for %s" />
<!-- when we would like to display only current page number -->
<page-info format="%1$s." />
<!-- or -->
<page-number />
<!-- when we would like to display only total pages number -->
<page-info format="%2$s pages" />
</div>
</header>
</placeholders>
Some text
</dynamic-page>
</pdf>
The "page" and "dynamic-page" tags can have a "document-template" attribute, that allows you to use an external pdf document as a template.
For the "page" tag, the page's template will be the first page of an external document.
For the "dynamic-page" tag, the template for each page will be the corresponding page of an external document.
Example:
<pdf>
<dynamic-page document-template="path/to/file.pdf">
<div>Some content</div>
</dynamic-page>
</pdf>
Page can be separated on columns:
<pdf>
<dynamic-page>
<column-layout>
<div width="100%" height="2500px" background.color="green">
</div>
</column-layout>
</dynamic-page>
</pdf>
The above XML describes several pages of the pdf document, with green rectangles separated on two columns.
The "column-layout" tag has three additional parameters: number-of-columns, margin-between-columns and equals-columns.
Default values for this attributes are 2, 10 and false respectlivy. If the equals-columns attribute is set, columns will have more or less equals height.
Page and column may by manually broken using one of these tags: page-break, column-break, break.
All these tags are the same. These tags need to be direct children of the breaking element (dynamic-page or column-layout).
If you want to avoid automatic page or column break on certain tags, you should set the "breakable" attribute of this tag to "off.
Example:
<pdf>
<dynamic-page>
<div breakable="false">this div won't be automatically broken</div>
</dynamic-page>
</pdf>
Metadata can be added by attributes at the document's root.
Supported metadata is: Creator, Keywords, Subject, Author, Title, ModDate, CreationDate and Trapped.
These attribute names are case sensitive.
Example:
<pdf Author="Piotr Sliwa" Title="Test document">
<!-- some other elements -->
</pdf>
The library has four primary config files that allow you to adopt the library for specyfic needs and extending.
In order to change default the config files, you must pass to Facade constructor configured Loader object:
$loader = new PHPPdf\Core\Configuration\LoaderImpl(
'/path/to/file/nodes.xml',
'/path/to/file/enhancements.xml',
'/path/to/file/fonts.xml',
'/path/to/file/colors.xml'
);
$facade = new PHPPdf\Core\Facade($loader);
If you want to change only one config file, you should use LoaderImpl::set* method:
$loader = new PHPPdf\Core\Configuration\LoaderImpl();
$loader->setFontFile(
'/path/to/file/fonts.xml'); //there are setFontFile, setNodeFile, setComplexAttributeFile and setColorFile methods
$facade = new PHPPdf\Core\Facade($loader);
FacadeBuilder can be uset to build and configure Facade. FacadeBuilder is able to configure cache, rendering engine and document parser.
$builder = PHPPdf\Core\FacadeBuilder::create(/* you can pass specyfic configuration loader object */)
->setCache('File', array('cache_dir' => './cache'))
->setUseCacheForStylesheetConstraint(true); //stylesheets will be also use cache
$facade = $builder->build();
Library supports basic (official) markdown syntax. To convert markdown document to pdf, you should configure Facade object by MarkdownDocumentParser. You also might to use FacadeBuilder to do this for you.
Example:
$facade = PHPPdf\Core\FacadeBuilder::create()
->setDocumentParserType(PHPPdf\Core\FacadeBuilder::PARSER_MARKDOWN)
->setMarkdownStylesheetFilepath(/** optionaly path to stylesheet in xml format */)
->build();
By default, in markdown pdf document, helvetica font is used. If you want to use utf-8 characters or customize a pdf document, you should provide your own stylesheet by using the FacadeBuilder::setMarkdownStylesheetFilepath method.
The stylesheet structure has been described in the stylesheet chapter. By default the stylesheet is empty, if you want to set another font type, the stylesheet should look like this:
<stylesheet>
<any font-type="DejaVuSans" />
</stylesheet>
Internally the MarkdownDocumentParser converts a markdown document to html (via the PHP markdown library), then converts html to xml, and at last xml to a pdf document.
Be aware of that, if you in a markdown document use raw html that will be incompatible with the xml syntax of PHPPdf (for example unexistend attributes or tags), the parser will throw an exception then.
Not all tags used in the markdown implementation are propertly supported by PHPPdf, for example "pre" and "code" tags.
For now "pre" tag is an alias for "div", and "code" tag is an alias for "span", be aware of that.
PHPPdf is able to generate image (jpg or png) files insted of a pdf document. To achieve that, you must configure the FacadeBuilder, example:
$facade = PHPPdf\Core\FacadeBuilder::create()
->setEngineType('image')
->build();
//render method returns array of images' sources, one pdf page is generated to single image file
$images = $facade->render(...);
By default the GD library is used to render an image.
But you can also use Imagick, which offers a better quality, so it is recommended that if you have the opportiunity to install Imagick on your server. To switch the graphic library, you must configure the FacadeBuilder object using the setEngineOptions method:
$builder = ...;
$builder->setEngineOptions(array(
'engine' => 'imagick',
'format' => 'png',//png, jpeg or wbmp
'quality' => 60,//int from 0 to 100
));
Supported graphic libraries are: GD (default), imagick, gmagick. PHPPdf uses the Imagine library as an interface for graphics file generation.
Below is a list of known limitations of the current version of the library:
This library works with php 5.3 and up.