This extension simplifies the integration of the TCPDF, FPDI and optional the yii extension pdfable into your application.
The combination with FPDI allows to read pages from existing pdf files and use them as templates to write into (letter headed paper,...).
The extension provides a base document class "EPDFFactoryDoc" where you can design your document without thinking about all the stuff around to integrate the libraries and instantiate the classes. The feature of caching of created pdf documents is supported.
Requirements ¶
- PHP 5.3+
- Developed with Yii 1.1.14 (not tested with older versions, but should work too)
- TCPDF not included.
- Optional: pdfable not included.
- Included: FPDI
Download the latest pdfFactory version from github.
Installation ¶
- Extract the files into protected/extensions/pdffactory
- Download TCPDF and extract the library into protected/extensions/pdffactory/vendors/tcpdf.
- Configure the application component EPdfFactory in your config file config/main.php
You can place the vendor libraries in another path, if you configure tcpdfPath and fpdiPath.
'import' => array(
...
'ext.pdffactory.*',
'application.pdf.docs.*', //the path where you place the EPdfFactoryDoc classes
),
'components' => array(
...
'pdfFactory'=>array(
'class'=>'ext.pdffactory.EPdfFactory',
//'tcpdfPath'=>'ext.pdffactory.vendors.tcpdf', //=default: the path to the tcpdf library
//'fpdiPath'=>'ext.pdffactory.vendors.fpdi', //=default: the path to the fpdi library
//the cache duration
'cacheHours'=>5, //-1 = cache disabled, 0 = never expires, hours if >0
//The alias path to the directory, where the pdf files should be created
'pdfPath'=>'application.runtime.pdf',
//The alias path to the *.pdf template files
//'templatesPath'=>'application.pdf.templates', //= default
//the params for the constructor of the TCPDF class
// see: http://www.tcpdf.org/doc/code/classTCPDF.html
'tcpdfOptions'=>array(
/* default values
'format'=>'A4',
'orientation'=>'P', //=Portrait or 'L' = landscape
'unit'=>'mm', //measure unit: mm, cm, inch, or point
'unicode'=>true,
'encoding'=>'UTF-8',
'diskcache'=>false,
'pdfa'=>false,
*/
)
),
),
The configured attributes above are only the default values. You can assign other values to these attributes before rendering a pdf.
You can use the composer do download/install the components, a composer.json file is included.
Usage ¶
Basic usage of TCPDF / FPDI instances ¶
Working with the raw tcpdf / fpdi instances is not the purpose of this extension. But if you need that, you can instantiate these classes like below.
//create a TCPDF instance with the params from tcpdfOptions from config/main.php
$pdf=Yii::app()->pdfFactory->getTCPDF();
//use this instance like explained in [TCPDF examples](http://www.tcpdf.org/examples.php "")
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Nicola Asuni');
$pdf->addPage();
$pdf->write(0,'Hello world');
...
$pdf->Output();
//create a TCPDF instance with other tcpdfOptions than the configured default.
$pdf=Yii::app()->pdfFactory->getTCPDF(array('format'=>'A5'));
//create a FPDI instance (always brigded mode so FPDI extends TCPDF)
//see [FPDI](http://www.setasign.com/products/fpdi/about/ "")
$pdf=Yii::app()->pdfFactory->getFPDI(); //other options like above
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Nicola Asuni');
//import the template
$pdf->setSourceFile('...path to pdf template file...');
$tplidx = $pdf->importPage(1);
$pdf->addPage();
$pdf->useTemplate($tplidx, 10, 10, 90);
$pdf->write(0,'Hello world');
...
$pdf->Output();
Using EPdfFactoryDoc ¶
Create predefined classes in the configured import path for your pdf docclasses to output the pdf with a few lines in your controller action.
The minimum methods you have to override from the parent are
- getPdfName()
- initDocInfo()
- renderPdf()
The renderPdf() must always start with the lines $this->addPage() and $this->getPdf();
class ProductPdf extends EPdfFactoryDoc
{
public function renderPdf()
{
$this->addPage();
$pdf = $this->getPdf();
...
}
...
}
But don't call output() in this method.
The EPdfFactoryDoc supports the methods setData(), getData() and getDataItem(). Data is a simple array you assign before pdf output. You have access to the data inside the doc class.
The method getPdf() return the TCPDF or FPDI instance. The type if instance depends on you have assigned a pdf template before output.
class ProductPdf extends EPdfFactoryDoc
{
// the pdf name on creating the (cached) file or downloading.
//always add the extension '.pdf'
public function getPdfName()
{
return $this->getDataItem('model')->title) . '.pdf';
}
// the info assigned to the pdf document
protected function initDocInfo()
{
$pdf = $this->getPdf();
$pdf->SetTitle('My company');
$pdf->SetSubject('Product description');
$pdf->SetKeywords('x, y, z');
}
public function renderPdf()
{
$this->addPage();
$pdf = $this->getPdf();
$pdf->SetFontSize(18);
$model = $this->getDataItem('model');
$pdf->Write(0, 'Article No. ' . $model->id);
//all code to render the pdf, but no output()
...
}
More methods to override - take a look at the source of EPdfFactoryDoc:
- initMargins()
- initFont()
- initHeader()
- initFooter()
Controller action: Render a pdf if the GET param 'pdf' isset.
public function actionProduct($id)
{
$model = Product::model()->findByPk($id); //load from db
if(isset($_GET['pdf']))
{
$productPdf = ProductPdf::doc();
$productPdf->setData(array('model'=>$model));
$productPdf->output(); //destination param = 'I' = standard output to the browser
//other destination params: 'F'=file, 'D'=download, 'S'=string ... see TCPDF docs
//$productPdf->output('D'); //enforce download
}
else
$this->render('product',array('model'=>$model));
}
If you want to assign a pdf file as template you can override the init() method of the EPdfFactoryDoc class, or you use the setTemplate() method after creating the doc.
If you assign a template on/after creating the doc, the internal created class on rendering will be a FPDI not a TCPDF. So you don't have to think about FPDI or TCPDF.
class ProductPdf extends EPdfFactoryDoc
{
public function init()
{
$this->setTemplate('productsheet.pdf', 0, 0, 210); //with defining the area to print in
//override the configured default
//$this->setTcpdfOptions(array('orientation'=>'L', ...);
}
...
}
or to be more flexible by assigning the template in the controller action.
public function actionProduct($id)
{
$model = Product::model()->findByPk($id); //load from db
if(isset($_GET['pdf']))
{
$productPdf = ProductPdf::doc();
$productPdf->setTemplate('productsheet1.pdf', 0, 0, 210); //maybe change by action params ...
//$productPdf->setTcpdfOptions(array('orientation'=>'L', ...);
...
}
More examples ¶
If you want to mail your generated pdf:
public function actionMailProduct($id)
{
$model = Product::model()->findByPk($id); //load from db
$productPdf = ProductPdf::doc();
$productPdf->setData(array('model'=>$model));
$pdfFile=$productPdf->output('F'); //file saved as configured pdfPath/pdfName
... code to attach the file to your mailer and send mail
}
Multipage pdf
If you want to render different products into one pdf:
class ProductPdf extends EPdfFactoryDoc
{
//render a single page for each model
protected function renderPage($model)
{
$this->addPage();
$pdf = $this->getPdf();
...
$pdf->Write(0, 'Article No. ' . $model->id);
...
}
public function renderPdf()
{
foreach($this->getDataItem('models') as $model)
$this->renderPage($model);
}
...
}
public function actionDownloadProducts()
{
$models = Product::model()->findAll(); //findByAttributes ...
$productPdf = ProductPdf::doc();
$productPdf->setData(array('models'=>$models));
$productPdf->output('D');
}
Caching ¶
If you have enabled the cache by configuring 'cacheHours' >= 0, a pdf file will be created after rendering: path/name with the configured pdfPath as path and the result from getPdfName() as name.
So if you call $productPdf->output('D'), the cache file will be created if the file not exists or is expired. On the next call, the pdf will not be rendered again, instead the cached file will be used to send to the browser. The same for all other destinations: F,I,...
Flushing the cache
Use the method flushCache() if the data for generating a pdf has been changed. For example in the method afterSave() of a model. Or add it to a controller action actionFlushPdfCache()
Yii::app()->pdfFactory->flushCache(); //all files
Yii::app()->pdfFactory->flushCache('invoice.pdf'); //a single file within the configured pdfPath = cachePath
More cache methods:
- isCached('invoice.pdf') check if a file is cached
- isCacheEnabled() check if the cache is enabled
- disableCache() temporary disable the cache
Extension pdfable ¶
If you have installed the extension pdfable, you can configure and use this extension inside the pdfFactory application component. The difference is, that you don't have to add the pdfable action to a controller, pdfFactory will do this. You can render a view/url inside every controller you like.
Add the extension to the pdfFactory by adding the addional keys: 'pdfableExt','htmlToPdfOptions' and 'htmlToPdfPageOptions' Take a look at the the docs of pdfable for the available options.
'components' => array(
...
'pdfFactory'=>array(
'class'=>'ext.pdffactory.EPdfFactory',
...
'pdfableExt'=>'ext.pdfable',
//the pdfable attributes
'htmlToPdfOptions' => array(
//'bin' => '/usr/bin/wkhtmltopdf',
// 'dpi' => 600,
);
//the pdfable page attributes
'$htmlToPdfPageOptions' => array(
// 'page-size' => 'A5',
// 'user-style-sheet' => Yii::getPathOfAlias('webroot').'/css/pdf.css',
);
),
Now you have the methods renderUrl() and renderView() available. Caching is not supported when calling these methods.
Work with the WkHtmlToPdf instance by calling $wkPdf = Yii::app()->pdfFactory->getWkHtmlToPdf();
Usage in a controller:
public function actionProduct($id)
{
$model = Product::model()->findByPk($id); //load from db
if(isset($_GET['pdf']))
{
Yii::app()->pdfFactory->renderUrl(null,array('id'=>$id)); //the current request url
}
else
$this->render('product',array('model'=>$model));
}
Or a other action method (maybe in another controller)
public function actionRenderProduktPdf(id)
{
Yii::app()->pdfFactory->renderUrl('products/product',array('id'=>$id));
//Yii::app()->pdfFactory->renderUrl('http://www.yiiframework.com/');
...
OR
$model = Product::model()->findByPk($id); //load from db
Yii::app()->pdfFactory->renderView('product',array('model'=>$model));
}
Resources ¶
Changelog ¶
- 1.0.1 bugfixes: issues when using multiple doc classes for output; multipage FPDI templates
- 1.0.0 initial release
Excelente
Muchas gracias lo estoy usando y funciona bien. Desconocía de estas extensiones. Parecen muy prometedoras. Sin embargo cuando le pongo cache = 5 y cargo la página no me visualiza de nuevo el pdf, pero me sale un error de que no puede leer el pdf, pero si puedo descargarlo sin problemas. No entiendo porque no se puede visualizar.
La solución que le di es verificar si existe en cache y si es así, visualizarlo manualmente en el navegador.
Gracias de nuevo!
Amazing!
Just downloaded it and works like a charm! congratulations to the team :)
Extension for PDFFactory
I have create extension baseon PdfFactory, it is pdffactoryHeart to Easy Export data from Gridview to PDF
For loop Multiple pdf files
How can I create multiple pdfs on a for loop?
I have this:
foreach ($elements as $element) { $accPdf = AccreditationPdf::doc(); $accPdf -> setData(array('model'=>$model, 'elements'=>$elements)); $pdfFile = $accPdf -> output('F'); ...
But i get: Undefined property: TCPDF::$h
If only one pdf, it works fine.
Multiple pdf files
Try something like below.
To ensure to create a unique filename for each, you can use a index or a unique value from the element.
$pdfFiles = array(); $index=0; foreach ($elements as $element) { $accPdf = AccreditationPdf::doc(); $accPdf -> setData( array('model'=>$model, 'element'=>$element, //$elements ?? 'index'=>$index) ); $accPdf -> renderPage(); $pdfFiles[] = $accPdf -> output('F'); $index++; }
In the AccreditationPdf override the method getPdfName() to generate the unique filename.
public function getPdfName() { return 'document_'.$this->getDataItem('index').'.pdf'; }
Already did that
Thank you for your answer! I was already doing that. The file name has the Id of the element in it.
I've found this post. But don't know how to apply it here. :S
P.S. plural elements was wrong. (It was a test were I created MultiplePages as a workaround for what I intended) :)
Should do the same
My code below should do nearly the same.
Take a look at the source.
always creates a new instance of AccreditationPdf.
In 'renderPage' you have to call
$pdf = $this->getPdf(); //create a new instance of TCPDF $this->addPage(); ... $pdf ->Write(...) $pdf ->Cell(...) .... //no pdf->output here
I print multiple pdf-tickets like my code example.
The error TCPDF::$h seems to an issue of your renderPage method with some 'element' values.
Please debug your code and step through your renderPage to see why some 'height' is undefinied.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.