Creating PDF files with CakePHP and TCPDF

By Kalileo (kalileo)
With CakePHP 1.2 creating PDFs with CakePHP has just gotten a lot easier. This tutorial shows how to combine CakePHP and the powerful TCPDF for easy PDF file creation.

TCPDF is an Open Source PHP class for generating PDF documents. It continues where FPDF stopped, and contains all its goodies plus support of UTF-8 Unicode and Right-To-Left languages! Especially the missing UTF-8 Unicode support in FPDF is a problem for everyone living outside the English language only countries.


Step 1: Download and install TCPDF

  1. Go to http://www.tcpdf.org and download the latest version of TCPDF.
  2. Extract to one of your vendors folders, such as app/vendors. It will create a directory tcpdf there with tcpdf.php and more in it. You need at least the folders tcpdf/config and tcpdf/fonts in your application.
  3. Configure TCPDF, see its documentation. You want at least to have a look at tcpdf/config/tcpdf_config.php.

Step 2: Extend TCPDF to customize your header and footer

There is a default header and footer in TCPDF, defined in a header() and a footer() method, which is supposed to be overwritten by you, if needed. This can be done by extending TCPDF and then calling this extended TCPDF class from your application.


In app/vendors create the file xtcpdf.php with this content:

Download code <?php
App
::import('Vendor','tcpdf/tcpdf');

class 
XTCPDF  extends TCPDF
{

    var 
$xheadertext  'PDF created using CakePHP and TCPDF';
    var 
$xheadercolor = array(0,0,200);
    var 
$xfootertext  'Copyright © %d XXXXXXXXXXX. All rights reserved.';
    var 
$xfooterfont  PDF_FONT_NAME_MAIN ;
    var 
$xfooterfontsize ;


    
/**
    * Overwrites the default header
    * set the text in the view using
    *    $fpdf->xheadertext = 'YOUR ORGANIZATION';
    * set the fill color in the view using
    *    $fpdf->xheadercolor = array(0,0,100); (r, g, b)
    * set the font in the view using
    *    $fpdf->setHeaderFont(array('YourFont','',fontsize));
    */
    
function Header()
    {

        list(
$r$b$g) = $this->xheadercolor;
        
$this->setY(10); // shouldn't be needed due to page margin, but helas, otherwise it's at the page top
        
$this->SetFillColor($r$b$g);
        
$this->SetTextColor(00);
        
$this->Cell(0,20''0,1,'C'1);
        
$this->Text(15,26,$this->xheadertext );
    }

    
/**
    * Overwrites the default footer
    * set the text in the view using
    * $fpdf->xfootertext = 'Copyright © %d YOUR ORGANIZATION. All rights reserved.';
    */
    
function Footer()
    {
        
$year date('Y');
        
$footertext sprintf($this->xfootertext$year);
        
$this->SetY(-20);
        
$this->SetTextColor(000);
        
$this->SetFont($this->xfooterfont,'',$this->xfooterfontsize);
        
$this->Cell(0,8$footertext,'T',1,'C');
    }
}
?>

Of course, customize this to show your organization's name etc., and modify the code as you like. See the TCPDF documentation for details.


Step 3: Create your layout for PDF

You cannot use your default layout, as it would wrap the PDF file in your HTML page code. You need a layout such as this one, save it as app/views/layouts/pdf.ctp :

Download code <?php
header
("Content-type: application/pdf");
echo 
$content_for_layout;
?>

Step 4: For your Controller

In your controller you will have a method which will output the PDF. This here is the code as it is used in one of my real world applications to print a nice PDF page with data and pictures about a property:


Download code
    function viewPdf($id = null)
    {
        if (!$id)
        {
            $this->Session->setFlash('Sorry, there was no property ID submitted.');
            $this->redirect(array('action'=>'index'), null, true);
        }
        Configure::write('debug',0); // Otherwise we cannot use this method while developing

        $id = intval($id);

        $property = $this->__view($id); // here the data is pulled from the database and set for the view

        if (empty($property))
        {
            $this->Session->setFlash('Sorry, there is no property with the submitted ID.');
            $this->redirect(array('action'=>'index'), null, true);
        }

        $this->layout = 'pdf'; //this will use the pdf.ctp layout
        $this->render();
    }

Adapt to your needs. The critical part is just to select the PDF layout before rendering.

Download code
        $this->layout = 'pdf'; //this will use the pdf.ctp layout
        $this->render();

Step 5: For your View


Here is where the magic happens. Because with CakePHP we can load the vendor directly in the view we do not need to wrap it in a helper. So the big TCPDF library with currently 9600 lines of code in the main class tcpdf.php alone will only get loaded when we really need it, that is when we actually create the PDF file. The vendor is now used here like an external helper. Note: I do not know if that was intended or not, but the more I think about it the more I like it, it's so elegant and efficient, and demonstrates the power and flexibility of CakePHP.


But enough said, here's the code for the view:


View Template:

Download code
<?php
App
::import('Vendor','xtcpdf'); 
$tcpdf = new XTCPDF();
$textfont 'freesans'// looks better, finer, and more condensed than 'dejavusans'

$tcpdf->SetAuthor("KBS Homes & Properties at http://kbs-properties.com");
$tcpdf->SetAutoPageBreakfalse );
$tcpdf->setHeaderFont(array($textfont,'',40));
$tcpdf->xheadercolor = array(150,0,0);
$tcpdf->xheadertext 'KBS Homes & Properties';
$tcpdf->xfootertext 'Copyright © %d KBS Homes & Properties. All rights reserved.';



// Now you position and print your page content
// example: 
$tcpdf->SetTextColor(000);
$tcpdf->SetFont($textfont,'B',20);
$tcpdf->Cell(0,14"Hello World"0,1,'L');
// ...
// etc.
// see the TCPDF examples 

echo $tcpdf->Output('filename.pdf''D');

?>

That was easy! Yes, that's all.


The Questions and Answers below are only of interest for users of the FPDF helper.


Why not FPDF?

For me the main reason is that there is no Unicode support. You can add a limited unicode support to it, as described on the Dievolution blog, by hacking the Cell method of FPDF, but then, why not go for TCPDF right away. No hack needed, and the TCPDF author, Nicola Asuni, is very active, releasing a new update almost every week.


How about the FPDF helper, as shown here in the bakery?

I used this helper quite a lot, it worked fine with CakePHP 1.1. Somewhere between the 1.2 beta and 1.2 RC it stopped working though. The reason IMHO is that it is not implemented correctly.

It extends the FPDF class directly, but a Helper should extend a Helper, such as the AppHelper class.


This is what the FPDF helper does, works worked with CakePHP 1.1, but wrong:

Download code class FpdfHelper extends FPDF

This would be correct but it does not work:

Download code class FpdfHelper  extends AppHelper

Somewhere in the CakePHP 1.2 development a change happened which was that helpers receive an array as first argument when they are initialized. A Helper which extends AppHelper expects that and handles it correctly, but FPDF does not know what to do with that array, as it expects as first argument the page orientation.


Can the FPDF Helper be hacked to continue working with CakePHP 1.2?

Yes, it can, but this should be not the solution, as it is not needed (as shown above). Simply add this line in the FPDF code (the one in your vendors directory, not the helper), as first line of the FPDF method, which is in my FPDF version at line 78:

Download code if (is_array($orientation)) return;
it will then be:
Download code
function FPDF($orientation='P',$unit='mm',$format='A4')
{
    if (is_array($orientation)) return;
    ...

This will make it ignore the Helper initialization, but let it run fine when it is called later, via

Download code $this->FPDF($orientation, $unit, $format); 

in the FPDF helper's setup() method.


Why is this FPDF hack not needed in CakePHP 1.2?

FPDF and TCPDF are external libraries, which you can integrate in CakePHP under Vendors. Now CakePHP 1.2 changed the way Vendors are included from


Download code vendor("fpdf/fpdf")  to
Download code App::import('Vendor','fpdf/fpdf');
- or for TCPDF: - 
App::import('Vendor','tcpdf/tcpdf');

This alone does not change too much though. Still, you would need a helper to wrap the TCPDF calls to use them in your view, similar to:

Helper Class:

Download code <?php  <?php
App
::import('Vendor','xtcpdf');

class 
TcpdfHelper extends AppHelper {
    var 
$pdf;
    function 
setup() {
        
$this->pdf = new XTCPDF();
    }
}

?>?>
and then call $tcpdf->pdf->whatevertcpdfmethodyouneed() from your view.

Fortunately this is not needed, because in CakePHP 1.2 RC2 you can now use App::import directly in the view. As shown above :)


 

Comments 771

CakePHP Team Comments Author Comments
 

Comment

1 TCPDF path configuration

Hi,
maybe you should spent a little more on how you set tcpdf paths in configuration file.
I tried to make it run bur I always got "TCPDF error: Could not include font definition file".
Nice post, really interesting.
Bye!!
Posted Aug 28, 2008 by Giovanni Casano
 

Comment

2 Error: Fatal error: Class 'XTCPDF' not found in C:\xampp\htdocs\cake12\CakePHP\app\views\books\view_pdf.ctp on line 3

Hi;

I followed the tutorial but having some problem:
Following is the error...

Fatal error: Class 'XTCPDF' not found in C:\xampp\htdocs\cake12\CakePHP\app\views\books\view_pdf.ctp on line 3



Posted Oct 23, 2008 by Muhammad Mohsin Ali
 

Question

3 Always get an empty page

I tried your tutorial to create PDFs with my cakrPHP application. The first try I use your code as you discribe above to create a simple PDF. But if i call the view, i get an empty page instead of the PDF. The only thing i changed is the classname for my own extended TCPDF class. Any hint? Has anyone else the same problem? If needed i can post the relevant code here.
Any help would be appreciated.

draikin
Posted Oct 29, 2008 by Thomas Heinrich
 

Comment

4 Reverse in Persian problem

when i use tcpdf directly ( samples in example folder ) i see the result right, but when i use it as helper in cake or vendor all of persian characters are reverse! can anyone help me?
Posted Oct 30, 2008 by Vahid Alimohammadi
 

Comment

5 I only get a blank page

First, thank you for this great tutorial. Unfortunatly it dosn't work for me. I tried your example code but only get an empty page. The only thing i changed was the class name for the extended vendor class. I don't if I miss anything. Has anyone a similar problem with the above code?
Posted Nov 2, 2008 by Thomas Heinrich
 

Comment

6 permissions

Hi Kalileo thanks for providing this - it seems cool.

I had a problem getting it to work at the beginning until I gave the whole tcpdf dir a 755 permission. the instructions are not clear on the TCPDF site about how to set the permissions and it would not work for me easily until I did this. I use XAMPP
Posted Nov 5, 2008 by Luke Barker
 

Comment

7 Only get an empty page

If i try the above example in my cakePHP application, the result is an empty page. No content, no download. Has anyone got a PDF page with the above code? Did i miss something? Any hints?

draikin
Posted Nov 13, 2008 by Thomas Heinrich
 

Comment

8 Hints to troubleshoot problems

If it doesn't work (ie. blank page instead of pdf) you may try checking these:
* www-data (or whatever user your webserver is running as) has read access to tcpfg/ -R
* increase memory limit in php.ini
* remove "echo" before "$tcpdf->Output"
Posted Nov 20, 2008 by Voro Experior
 

Bug

9 No 'Hello, world' until I added AddPath()...

THANKS HEAPS!!!! This got my App PDFing within a couple days.

Example worked OK, but didn't see "Hello, world." until I added $tcpdf->AddPath(); in the view in front of the SetTextColor() bit.

Also, it showed up inside the header, until I repositioned it from Cell(0,14, ...); to Cell(75,75, ...); May just need $tcpdf->lastPath(); ahead of the AddPath();

Using writeHTML() for now to keep it 'easy' making invoices and POs, but when I learn about headers and footers, I'll figure out why "Hello, world" was inside the header...

regards,
oh4real
Posted Dec 24, 2008 by ohforreal
 

Comment

10 Great article

Followed this through and got PDFs generating from my model data in no time. There are lots of easy examples on the tcpdf website. Well Done!!!
Posted Jan 7, 2009 by Paul Rhodes
 

Comment

11 Great article

I need this indeed. Great article. Thanks!
Posted Feb 13, 2009 by KANG Ghee Keong
 

Question

12 How to print table data?

Thanks, this is a very interesting article about howto integrate things like fpdf or tcpdf into cake infrastructure, I am really glad, that you wrote this, as it is not possible to understand from the manuals or any book out there how this could be achieved. Thanks!
However, it would be very nice to go one step further and show how to use this thing in a more productive way - say how would you print your models to pdf? How would you print tabe data?
Posted Mar 2, 2009 by Johnny Cake
 

Comment

13 Why not as a component? Help fight climate warming!

There is one important idea in this tutorial: generate the pdf only when needed. So why do you want to generate the pdf over and over again for every request? On a site with many visitors this will take your server down, especially if you are using the bloated tcpdf lib and not fpdf. You will especially have much fun if a spider follows all your "generate pdf"-links. You NOT have to generate the pdf on every request, but only when the content you want to output has changed. A lot of cpu cycles could be saved that way and climate will stay cool.
Of course this depends on your content, but if you have an article or a newsitem or a tutorial like this page and the content will not change very fast or maybe never - it would be much better design to generate the pdf after inserting or updating and keep the generated pdf downloadable and link to it.
I do not understand cake very well right now, but I think a component would be the right place for this kind of thing... I will check that out later.
Posted Mar 4, 2009 by Johnny Cake
 

Comment

14 Don't forget AddPage()

Thank you for the great article. I had some trouble getting this to work in that the header and footer were there, but no text I added. This was until I found that adding:

$tcpdf->AddPage();

in the view right before:

$tcpdf->SetTextColor(0, 0, 0);

in the view fixed it.
Posted Apr 2, 2009 by Al Scheuring
 

Comment

15 Apology

I have to apologize to all commenters here, it took me until now to see that I have to click on view comments in the my account page to see (and publish) your comments. Sorry Bakers!
Posted Apr 26, 2009 by Kalileo
 

Comment

16 @Muhammad Mohsin Ali:

you need to call in your view

View Template:


App::import('Vendor','xtcpdf');
before you call

View Template:


$tcpdf = new XTCPDF(); 
If xtcpdf.php with the Class XTCPDF is in app/vendors, and no typos etc, then it will be found.
Posted Apr 26, 2009 by Kalileo
 

Comment

17 Problems with some TCPDF versions

@Thomas Heinrich:
A few moths ago I needed to move a site with this code running from one server to another, and used the opportunity to update cake and tcpdf. All fine on the development server, but not on the production server.

Similar problems as you saw, it just didn't work, something with the fonts was messed up. It printed lines, borders, but no fonts. Which is kinda stupid :(

The solution was to rollback TCPDF to the old version. And again, working like a charm :) for an example see http://kbs-properties.com/properties/view/46 - click on the PDF icon at the top right above the content.

The working version was TCPDF 4.0.017 with release date 2008-08-05, the problematic one TCPDF 4.5.023 with release date: 2009-03-06.
Posted Apr 26, 2009 by Kalileo
 

Comment

18 clearing header area

@ohforreal: Yes, keeping the header and footer area clear is tricky, IIRC this code, before starting to print something on the page, helped:

View Template:


$pdf->SetAutoPageBreak( true, 25 );
$pdf->SetTopMargin(34);
Posted Apr 26, 2009 by Kalileo
 

Comment

19 How to print table data?

@Johnny Cake: you can treat such a PDF view the same way you treat a regular view, so you use the same logic and procedure to get your data ready to show. Then you pack it either in the TCPDF cell/multicell/write methods or you pack it into html code and print that using the TCDPF methods for printing HTML. There are examples in the TCPDF code / on the TCPDF website.
Posted Apr 26, 2009 by Kalileo
 

Comment

20 Saving CPU cycles

@Johnny Cake: with the App::import('Vendor','xtcpdf'); in the view TCPDF will only get loaded when you use it. I don't see how that can get reduced ;) except you could additionally wrap it in code to cache it, Cake makes that easy.

However, as you say, such code should only be used for dynamic data, such as vouchers, or price lists with limited validity. If you have static data, then offer a static PDF file for download.

FPDF has a problem with unicode, it cannot mix e.g. English and Thai. A big thank to Nicola Asuni to have bloated ;) TCPDF to fix that..
Posted Apr 26, 2009 by Kalileo
 

Comment

21 AddPage()

@Al Scheuring: yes, thanks for mentioning that! In the meantime also my code has $pdf->AddPage(); exactly at the place you specify, however when I wrote the article it was not there, so it seems to have worked without.

As mentioned earlier, TCPDF is seeing a lot of development, so that might be just one visible result.

The code in the view, as I use it now, has changed to:

View Template:


App::import('Vendor','xtcpdf');
$pdf = new XTCPDF();
$textfont      = 'freesans'; // looks better, finer, and more condensed than 'dejavusans'

$footerHeight = 25;

$tcpdf->SetAuthor("KBS Homes & Properties at http://kbs-properties.com ");
$tcpdf->SetAutoPageBreak( true, $footerHeight );
$tcpdf->SetTopMargin(34);
$tcpdf->setHeaderFont(array($textfont,'',40));
$tcpdf->xheadercolor = array(150,0,0);
$tcpdf->xheadertext = 'KBS Homes & Properties';
$tcpdf->xfootertext = 'Copyright © %d KBS Homes & Properties. All rights reserved.';

// Now you position and print your page content
// example: 
$tcpdf->AddPage();
$tcpdf->SetTextColor(0, 0, 0);
$tcpdf->SetFont($textfont,'B',20);
$tcpdf->Cell(0,14, "Hello World", 0,1,'L');
// ...
// etc.
// see the TCPDF examples 

echo $tcpdf->Output('filename.pdf', 'D');
?>
Posted Apr 26, 2009 by Kalileo
 

Question

22 Download error

Great tutorial. Thanks.
Using the code as is in this tutorial, I see only this error message:
"  TCPDF ERROR: Some data has already been output, can't send PDF file". Only when I change the output type 'D' to 'S', in the Output method of the $tcpdf method, can I see the PDF file in the browser.
I have reviewed the files of PHP controllers, models and helpers, and I've removed all the spaces that were before " What can be wrong?
Posted May 11, 2009 by Claudio Juan Böhm
 

Comment

23 < >

.

The comment above was posted May 11, 2009 by Claudio Juan Böhm but it is not displayed completely because he uses < > in it and the bakery comment validation lets it through without sanitation. Konqueror gets a big hiccup, Firefox not so much. Anyway, see the missing part in the quote in the next comment, which is my reply to it.
Posted May 12, 2009 by Kalileo
 

Comment

24 @Claudio Juan Böhm

Great tutorial. Thanks.
:)
Using the code as is in this tutorial, I see only this error message:
"  TCPDF ERROR: Some data has already been output, can't send PDF file". Only when I change the output type 'D' to 'S', in the Output method of the $tcpdf method, can I see the PDF file in the browser.
I have reviewed the files of PHP controllers, models and helpers, and I've removed all the spaces that were before <? Php  , but I do not get a direct download of the PDF file.
What can be wrong?

You mention that you checked "controllers, models and helpers", but how about view and layout? See the pdf.ctp in app/views/layouts/pdf.ctp which should look as described above, and then set in the controller with $this->layout = 'pdf'; before calling $this->render();
oh yes, and check also for spaces after the PHP closing tag, not just before the PHP opening tag
Posted May 12, 2009 by Kalileo
 

Comment

25 about __view() and AddPath()

In my tests, first I had the so-called blank screen. The first procedure to find the problem is re-enabling the debug:

// Configure::write('debug',0)
The first problem was caused by this line:

$property = $this->__view($id); // here the data is pulled from the database and set for the view
This '$this->__view()' function is an author function, not Cake native, as I'd supposed it was. This is the error:

Fatal error: Call to undefined method CidadesController::__view() in ...app\controllers\my_controller.php on line ###
The second detail is about using the AddPath() in the view:

$tcpdf->AddPath();
Because it generates this error:

Fatal error: Call to undefined method XTCPDF::AddPath() in ...\app\views\my_views\view_pdf.ctp on line 17
This can resolve the problem of the blank screen.

I hope this comment could be useful to save time. These little details make programmers lose hours or even days to find the solution.

Sorry for the mediocre english :P
Posted May 13, 2009 by Leonel Sanches
 

Comment

26 $this->__view($id);

@Leonel Sanches

The first problem was caused by this line:

$property = $this->__view($id); // here the data is pulled from the database and set for the view
This '$this->__view()' function is an author function, not Cake native, as I'd supposed it was.

Of course it is! Please have a look at whats written right above that sample code:

In your controller you will have a method which will output the PDF. This here is the code as it is used in one of my real world applications to print a nice PDF page with data and pictures about a property

So in your controller you need to do whatever is needed to pull any data you might want to publish from your database and set it.

In my application the "view" and the "viewPdf" share the same data, and while the "view" optimizes it for screen display, "viewPdf" optimizes for PDF display. They share the same code to pull and set the data, which I have combined in $this->__view($id); as said there:

// here the data is pulled from the database and set for the view


So adapt that to your data and your needs please.

The second detail is about using the AddPath() in the view:
$tcpdf->AddPath(); Because it generates this error:
Fatal error: Call to undefined method XTCPDF::AddPath() in ...\app\views\my_views\view_pdf.ctp on line 17

As you might have seen, my sample code above does not contain $tcpdf->AddPath() so I don't know where and why you have added it.

There are many different versions of TCPDF out there, and many different server environments. Some seem to need it (see comment 9 from Dec 24, 2008 by ohforreal), others not.

In any case, if you get a blank screen, your approach to re-enable debug output by commenting the line


Configure::write('debug',0)

is a good and fast way to see any error messages.
Posted May 19, 2009 by Kalileo
 

Comment

27 View to pdf

how can we conver view to pdf?
Posted May 22, 2009 by umit celik
 

Comment

28 @umit celik

Umit, that's what this article is all about, and the view part is described above in step 5.

If you explain where you're stuck I can try to answer more specific.
Posted May 23, 2009 by Kalileo
 

Comment

29 TCPDF 4.6.013 needs PHP set to more than 16 MB

The code as shown in this article works with the current TCPDF version, TCPDF 4.6.013 without any change.

However if you want to use one of the best features, Unicode support, and the included font which supports it best, freeserif, you might run into internal server errors, resulting in nice white pages. The reason is that freeserif has been doubling its size, probably supporting more and more, but with the side effect of using more memory. if your server is configured to allow PHP 16 MB RAM, then that is not enough.

Even without using freeserif, but freesans, after half a page of content the same error seems to be happening.

Either adjust the value in your php.ini on your server, or add the line


php_value memory_limit 36M

to your .htaccess and all is fine, the error is gone and the pages are not so white anymore.

Posted May 31, 2009 by Kalileo
 

Comment

30 Output files don't seem to be recognized by adobe

Everything seems to be working OK Cake-wise, except the resulting files can not be opened by Adobe reader. Do you think this is something wrong with TCPDF? Any clues?

The first few lines of the pdf file when I open it up in a text editor look like:

  %PDF-1.7
3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 595.28 841.89]
/Resources 2 0 R
/Contents 4 0 R>>
endobj
4 0 obj
<</Filter /FlateDecode /Length 245>>
stream
Posted Jun 14, 2009 by killian tobin
 

Comment

31 scratch my last comment

Thanks for the great article!

my version Adobe Reader was the problem.
Posted Jun 14, 2009 by killian tobin