Abstracts different PDF generation libraries. Currently supports TCPDF, Zend PDF and wkhtmltopdf.
This bundle is under active development and is prone to backwards compatibility breaks.
Using composer, in your project's root directory:
composer require "orkestra/pdf-bundle 1.0.x-dev"
Update AppKernel.
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Orkestra\Bundle\PdfBundle\OrkestraPdfBundle(),
// ...
);
}
In addition, you should add TCPDF or Zend PDF to your project, as necessary.
If planning to use wkhtmltopdf, ensure it is installed on your system.
No configuration is necessary, unless you want to change default settings.
Configuration defaults reference:
# config.yml
orkestra_pdf:
# Main cache directory
cache_dir: %kernel.cache_dir%/orkestra_pdf
tcpdf:
# Path to the tcpdf installation
root_dir: %kernel.root_dir%/../vendor/tecnick.com/tcpdf
# Path to tcpdf fonts
fonts_dir: %kernel.root_dir%/../vendor/tecnick.com/tcpdf/fonts
# Path to tcpdf images
image_dir: %kernel.root_dir%/../vendor/tecnick.com/tcpdf/images
wkhtmltopdf:
# Path to the wkhtmltopdf binary
binary_path: wkhtmltopdf
This bundle's aim is to provide a uniform API for generating PDFs using different generation tools.
Currently supported: TCPDF, Zend PDF, and wkhtmltopdf.
The basis of this bundle revolves around services called "Generators". Generators know how to take
some input, such as entities and other data, and return a PDF ready to be used. Generators encapsulate
library-specific logic, returning a subtype of Orkestra\Bundle\PdfBundle\Pdf\PdfInterface
which can
then be used to send the generated PDF to the browser, save it to the filesystem, etc.
All Generators should implement PdfGeneratorInterface
. Additionally, a base class called
AbstractPdfGenerator
is included. This document details how to extend AbstractPdfGenerator
The two main methods to implement are: doGenerate
and setDefaultParameters
.
This method actually performs the PDF generation. It takes two parameters, $parameters
and
$options
.
$parameters
is an array of data to be sent to the templating engine.$options
is an array of options to pass to the underlying PDF library.
This method configures an OptionsResolver
and allows specification of required and available
parameters that the Generator supports.
NOTE: There's also a setDefaultOptions
method available. Parameters are intended to be passed
to the templating engine, things like entities and collections. Options are intended to allow
configuration of the generator at runtime.
In this example, we use the WkPdf adapter and the built in templating engine to render a PDF.
<?php
namespace MyBundle\PdfGenerator;
use Orkestra\Bundle\PdfBundle\Generator\AbstractPdfGenerator;
use Symfony\Component\OptionsResolver\OptionsResolver;
class InvoiceGenerator extends AbstractPdfGenerator
{
/**
* Performs the PDF generation
*
* @param array $parameters An array of parameters to be used to render the PDF
* @param array $options An array of options to be passed to the underlying PdfFactory
*
* @return \Orkestra\Bundle\PdfBundle\Pdf\PdfInterface
*/
protected function doGenerate(array $parameters, array $options)
{
// Use the createPdf method to create the desired type of PDF
$pdf = $this->createPdf('wkpdf', $options);
// Call any native methods on the underlying library object
$builder = $pdf->getNativeObject();
$builder->useTemporaryFile();
$builder->setInput($this->render('MyBundle:Pdf/Invoice:template.html.twig', $parameters));
// Return the original PDF, calling getContents to retrieve the rendered content
return $pdf;
}
/**
* Configure the parameters OptionsResolver.
*
* Use this method to specify default and required options
*
* @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
*/
protected function setDefaultParameters(OptionsResolver $resolver)
{
$resolver->setRequired(array(
'invoice',
));
$resolver->setAllowedTypes(array(
'invoice' => 'MyBundle\Entity\Invoice',
));
}
}
AbstractPdfGenerator->createPdf($type, $options)
wraps the PDF factory registry. As the first
parameter, pass the type of PDF and options to configure it. Available PDF types: tcpdf
, wkpdf
,
and zendpdf
.
AbstractPdfGenerator->render($template, array $parameters = array())
uses the application's templating
engine to render templates for use in generating the content for the PDF.
In services.yml:
my_bundle.invoice_pdf_generator:
class: MyBundle\PdfGenerator\InvoiceGenerator
arguments: [ @orkestra.pdf.factory_registry, @templating ]
AbstractPdfGenerator
by default, takes a PDF factory registry and the templating service. You may
need to add more dependencies, depending on your implementation.
From within one of your controllers:
class MyController extends Controller
{
// ...
public function someAction()
{
// Fetch the invoice from somewhere
$invoice = $this->getInvoice();
$generator = $this->get('my_bundle.invoice_pdf_generator');
$pdf = $generator->generate(array('invoice' => $invoice));
// Write the PDF to a file
file_put_contents('/some/path/to.pdf', $pdf->getContents());
// Output the PDF to the browser
return new Response($pdf->getContents(), 200, array('Content-type' => 'application/pdf'));
}
}
TCPDF can be difficult to use, especially when rendering HTML. Usually, the easiest way is to
position different smaller sections of the page, making multiple writeHTML
or writeHTMLCell
calls on the TCPDF object.
Here's an example of the same Invoice Generator from before.
class InvoiceGenerator extends AbstractPdfGenerator
{
protected function doGenerate(array $parameters, array $options)
{
$pdf = $this->createPdf('tcpdf', $options);
/** @var \TCPDF $builder */
$builder = $pdf->getNativeObject();
$builder->addPage();
$builder->writeHtmlCell(0, 0, 0, 20, $this->render('MyBundle:Pdf/Invoice:invoiceHead.html.twig', $parameters));
$builder->writeHtmlCell(0, 0, 20, 43, $this->render('MyBundle:Pdf/Invoice:invoiceIntro.html.twig', $parameters));
$builder->writeHtmlCell(0, 0, 20, 66, $this->render('MyBundle:Pdf/Invoice:invoiceBody.html.twig', $parameters));
$builder->writeHtmlCell(0, 0, 20, 165, $this->render('MyBundle:Pdf/Invoice:invoiceFooter.html.twig', $parameters));
$builder->line(0, 190.5, 215.9, 190.5, array('dash' => 3));
$builder->writeHtmlCell(0, 0, 21, 200, $this->render('MyBundle:Pdf/Invoice/Detachment:detachmentCompany.html.twig', $parameters));
$builder->writeHtmlCell(0, 0, 0, 200, $this->render('MyBundle:Pdf/Invoice/Detachment:detachmentHead.html.twig', $parameters));
$builder->writeHtmlCell(0, 0, 19.5, 230, $this->render('MyBundle:Pdf/Invoice/Detachment:detachmentInfo.html.twig', $parameters));
$builder->writeHtmlCell(0, 0, 22, 210, $this->render('MyBundle:Pdf/Invoice/Detachment:detachmentDetails.html.twig', $parameters));
return $pdf;
}
}
See TCPDF documentation for more details.
TCPDF offers the ability to extend the TCPDF class directly to implement, for example, page headers and footers.
The default TcPdfFactory
lets you specify a custom subclass by specifying the 'className'
option when creating a PDF. For example:
class MySpecialGenerator extends AbstractPdfGenerator
{
protected function doGenerate(array $parameters, array $options)
{
$options['className'] = 'My\Bundle\CustomTcpdf';
$pdf = $this->createPdf('tcpdf', $options);
/** @var \My\Bundle\CustomTcpdf $builder */
$builder = $pdf->getNativeObject();
// Use your custom subclass as necessary...
return $pdf;
}
}
Notice that $builder
in the example above is an instance of My\Bundle\CustomTcpdf
.