Jump to content

How to Generate a PDF With PHP

+ 2
  adfm's Photo
Posted Apr 28 2010 06:41 AM

Creating a PDF with PHP can be relatively straightforward. However, knowing what library to use and how to deal with errors can help you cut back on the tedious work associated with generating a PDF. This excerpt from Peter MacIntyre's PHP: The Good Parts will walk you through the process and clue you in on a few tips along the way.


Adobe’s Portable Document Format (PDF) files have almost become the standard for preparing well-formatted documents. There are PDF readers/displayers for most web browsers, so there is no real excuse for not providing this kind of formatted document to your users if your web application demands its use. Standardized forms and statistical reports can all be drawn from a web system’s data, so it makes sense to format that data in a common layout.

There is a PHP add-on library that allows for the generation of dynamic PDF-formatted output. There are actually a few such PHP libraries out there, but we will look at one of the most widely used libraries, called FPDF. This library is also object-based and you can include it in your scripts the same way that you include the PHPMailer library.

To get started, here is a simple example of some code that will perform three simple tasks: create a blank PDF document, add some text to it, and display it:


require("../../fpdf/fpdf.php");



$pdf = new FPDF( );

$pdf->AddPage();

$pdf->SetFont('Arial','B',16);

$pdf->Cell(0,10,'PHP - The Good Parts!');

$pdf->Output();

As you can see from this code listing, after requiring the library file, we instantiate an object of the FPDF class and call it $pdf. Then we add a page with the AddPage method, set our output font, define the cell (location) for our string of output, and then—using the Output method—display the PDF in the browser. The actual browser output will look like that shown in Figure 8.1.


Note

When building these PDF pages in code you may get the following error:

“FPDF error: Some data has already been output, can’t send PDF file.”

This just means that there is already some output being sent to the browser. You can either find and remove the extraneous output or work around it by using the ob_start and ob_end_flush function combination at either end of your code listing.


Figure 8.1. Output of simple PHP-generated PDF

Attached Image


There is a concept in FPDF called the document’s cell. This refers to a rectangular area on the page that you can create and control. The cell can have a height, width, border, and, of course, text. The basic syntax for the cell method is as follows:


Cell(float w [, float h [, string txt [, mixed border

	[, int ln [, string align [, int fill [, mixed link]]]]]]])

The options for the Cell method call are width of the cell, height of the cell, text to be included in the cell, cell border (if desired), a new line control, alignment of the text within the cell, the cell’s fill color (if desired), and an HTML link if the text is to be a link to a web resource.


Note

If you leave the cell width (first attribute) at 0, the cell will take the entire width of the defined page. This only really becomes apparent when you turn on the border (as shown in the following example) or if you want different-sized cells on the same plane. In the latter case, you would potentially see the text overlapping.


So, for example, if we want to change our original example to have a border and be aligned to the right, we would change the method call to the following:


$pdf->Cell(0,10,'PHP - The Good Parts!' ,1 ,0 ,'R');

This would produce the browser output shown in Figure 8.2.


Figure 8.2. PHP PDF output right aligned, with border

Attached Image

The Cell method is the workhorse for output onto a PDF document and is used extensively while generating PDF documents with FPDF, so you would be well-served if you spent the time needed to learn the ins and outs of this method.

The new line control option of this method (and other FPDF methods) is important to understand. It controls the positioning of the writing cursor after the method is completed. In the above case, it is set to 0, which means that the write cursor will stay on the same line when it is finished and any subsequent writing will also take place from the left margin, potentially causing overlapping of output. If, however, it is set to 1, the write cursor will move to the next line as defined by the height attribute of the previous method call.

There is another FPDF method that comes in handy when you are trying to place separate data on the same output line, and it is called SetX. This moves the write cursor from the left margin by a set distance (we will talk about the measurement attributes in the next section). This may sound a little confusing, so let’s look at two simple examples with two Cell method calls each. The first example will leave the write cursor on the same line, and the second example will move it to the next line.


Here is the code and browser image (Figure 8.3) for the first example:

require("../../fpdf/fpdf.php");



$pdf = new FPDF( );

$pdf->AddPage();

$pdf->SetFont('Arial','B',16);

$pdf->Cell(10,10,'PHP - The Good Parts!', 0,0,'L');

$pdf->SetX(90);

$pdf->Cell(90,10,'Beware the Ides of March!', 1,0,'C');

$pdf->Output();

Figure 8.3. PDF output of two text blocks on the same line

Attached Image

Note

You may want to disable your browser’s caching capabilities while you are developing and testing the layout of your FPDF PDFs because some browsers will not reload the page with changes if the changes are so small that they don’t register as such with the cache control.


And here is the code and browser output (Figure 8.4) for the second example (we do not need to use the SetX method here, as we are moving the write cursor to the following line as part of the cell method call):


require("../../fpdf/fpdf.php");



$pdf = new FPDF( );

$pdf->AddPage();

$pdf->SetFont('Arial','B',16);

$pdf->Cell(10,10,'PHP - The Good Parts!', 0,1,'L');

$pdf->Cell(90,10,'Beware the Ides of March!', 1,0,'C');

$pdf->Output();

Figure 8.4. PDF output of two text blocks on separate lines

Attached Image

Constructor Method and Basic Document Options

As mentioned earlier, there are different settings for the measurement units on the PDF pages within FPDF. You can control them by sending parameters to the constructor when you instantiate a new copy of the class. Previously, you saw the $pdf = new FPDF( ); statement of instantiation, which creates an object with the default attributes. You can send the following parameters into the constructor:

  • The page orientation has the options of portrait (P) or landscape (L), portrait being the default.

  • The units of measurement have the options of point (pt), millimeter (mm), centimeter (cm), and inches (in), with millimeter as the default.

  • The page size has the options of A3, A4, A5, Letter, and Legal, with A4 as the default.

Here is a constructor call that defines portrait, inches, and letter layout:


$pdf = new FPDF('P', 'in', 'Letter' );

Note

You can even define a custom page layout, if you want, by sending an array of dimensions into the constructor in place of the third parameter. Business cards or special fliers, for instance, could have their own page dimensions. This constructor call will define a page that is 4 × 5 inches and landscape orientation:


$pdf = new FPDF('L', 'in', array(4,5));

Adding Document Headers and Footers

Let’s take a look at object inheritance, or extension, in action. Naturally, there is often a need for headers and footers on a multipage PDF document. FPDF has empty header and footer methods, and they are called automatically each time the AddPage method is called. Without extending the class and adding content to our own methods of the same names, however, nothing is visually apparent. So, let’s extend the class and add custom page header and footer methods to the child class. Here is the code:


require("../../fpdf/fpdf.php");



class myPDF extends FPDF {



	public $title = "FPDF Sample Page Header";



	//Page header method

	function Header() {



 	$this->SetFont('Times','',12);

 	$w = $this->GetStringWidth($this->title)+150;

 	$this->SetDrawColor(0,0,180);

 	$this->SetFillColor(170,169,148);

 	$this->SetTextColor(0,0,255);

 	$this->SetLineWidth(1);

 	$this->Cell($w,9,$this->title,1,1,'C',1);

 	$this->Ln(10);



	}



	//Page footer method

	function Footer() 	{

 	//Position at 1.5 cm from bottom

 	$this->SetY(-15);

 	$this->SetFont('Arial','I',8);

 	$this->Cell(0,10,'page footer -> Page '

 	.$this->PageNo().'/{nb}',0,0,'C');

	}



}



$pdf = new myPDF('P', 'mm', 'Letter');

$pdf->AliasNbPages();

$pdf->AddPage();

$pdf->SetFont('Times','',24);

$pdf->Cell(0,0,'text at the top of the page',0,0,'L');

$pdf->ln(225);

$pdf->Cell(0,0,'text near page bottom',0,0,'C');

$pdf->AddPage();

$pdf->SetFont('Arial','B',15);

$pdf->Cell(0,0,'Top of page 2 after page header',0,1,'C');

$pdf->Output();

There are a number of other methods being called from within the extended header and footer methods. Some of this is included here to show you that the entire class is inherited and not just the header and footer methods. Also, some of the methods are used to show the difference between the header and footer areas distinctly. The full listing of methods and their uses can be found on the product web page under the “Manual” link. The image shown in Figure 8.5 is the result of the above code being executed within the browser. It is a picture of the bottom of one page (to show the footer) and the top of the next page (to show the header).


Note

You can suppress the header or footer on a certain page by querying the value of the page number with the returned value from the PageNo() method and reacting appropriately. Make sure to use the AliasNbPages method before you add your first page to the document so that FPDF can count the pages being created.


Figure 8.5. Generated PDF with page headers and footers

Attached Image

Adding Images and Links


You can also add image and link content to PDF files with the FPDF library. These links can be anchors within the PDF file itself or full URL resources on the Web. First, we will look at inserting images into the PDF file, then we will look at making links from either images or text.


To add an image to the document, simply use the image method. In the following code, we will use the image method within the header method to add a PHP logo to the page header and remove the background fill color option from the cell method call so that we can see the image. The image parameters are the image filename, the x and y coordinates of the image, and the width and height of the image:


function Header() {



	$this->SetFont('Times','',12);

	$w = $this->GetStringWidth($this->title)+150;

	$this->SetTextColor(0,0,255);

	$this->SetLineWidth(1);

	$this->Image('phplogo.jpg',10,10.5,15,8.5);

	$this->Cell($w,9,$this->title,1,1,'C');

	$this->Ln(10);



}

The PDF document now looks like the image shown in Figure 8.6.

Figure 8.6. Generated PDF with image included in the page header

Attached Image


Now, to make this image link to the PHP home page, simply add the URL to the last parameter of the image method, skipping the type parameter with a comma.


Note


It’s a good idea to save the URL text to a string variable and then use that in the method parameter listing; this will make it easier to read and possibly reuse if there are other links using the same URL at other places in your document.


The new linking method call now looks like this:

$php_url = "http://www.php.net" ;

$this->Image('phplogo.jpg',10,10.5,15,8.5,"",$php_url);

The image is now a clickable link image, as shown by the hover text in Figure 8.7.


Figure 8.7. Inserted image with active URL link

Attached Image


The other kind of link that we can add to the PDF document is a link to another location within the document. This is the concept of a table of contents or an index. Creating an internal link is done in two parts. First, you define the origin for the link (the link itself), then you set the anchor (the destination that the link will take you to when you click it).


To set the origin of a link, use the AddLink() method. This method returns a handle for use when creating the destination portion of the link with the SetLink() method, which takes the origin’s link handle as its parameter so that it can perform the connection between the two items. Here is some sample code that performs the creation of both the origin and the destination parts; notice the use of the FPDF write method, which is another way to send text to the document (as opposed to using the cell method):


require("../../fpdf/fpdf.php");



$pdf = new FPDF();



//First page

$pdf->AddPage();

$pdf->SetFont('Times','',14);



$pdf->write(5,'For a link to page 2 - Click ');

$pdf->SetFont('','U');

$pdf->SetTextColor(0,0,255);

$link_to_pg2 = $pdf->AddLink();

$pdf->write(5,'here',$link_to_pg2);

$pdf->SetFont('');



//Second page

$pdf->AddPage();

$pdf->SetLink($link_to_pg2);

$pdf->Image('phplogo.jpg',10,10,30,0,'','http://www.php.net');

$pdf->ln(20);

$pdf->SetTextColor(1);

$pdf->Cell(0,5,'This is a link and a clickable image', 0, 1, 'L');

$pdf->SetFont('','U');

$pdf->SetTextColor(0,0,255);

$pdf->Write(5,'www.oreilly.com','http://www.oreilly.com');

$pdf->Output();

The browser outputs for both the link and the destination page for this code are shown in Figures 8.8 and 8.9.


Figure 8.8. PDF document generated with an internal link

Attached Image


Figure 8.9. Anchor page of PDF sample code with additional links

Attached Image


Adding a Watermark


The next feature that we’ll look at is making a watermark appear on the PDF document. This can be a nice addition to generated reports or sales brochures that you may want to create within a PHP application. Here is the code to create the watermark:


require("../../fpdf/fpdf.php");



$pdf = new FPDF( );

$pdf->AddPage();

$pdf->SetFont('Arial','B',16);

$pdf->SetXY(26,100);

$pdf->image('php_watermark.jpg');

$pdf->SetY(35);

$text = "This is sample text to show the watermark underneath it.";

for($i = 0; $i < 35; $i++) { $pdf->Cell(0,5,$text,0,1); }

$pdf->Output();

All that is really going on here is that we are moving the write cursor around the page with the SetXY and SetY methods, and we have an image that is set to a semitransparent shading level. There is really no difference here from setting an image on the page, except that we are overwriting the image with additional text. If this had not been a semitransparent image, the text and the image would be garbled together and it would look like a mess.


Make sure you add the image to the document first in the case of a watermark, as the last items sent to the document will overwrite anything previously sent. In Figure 8.10, if the text were sent out first, followed by the image, the image would overlay the text.


Figure 8.10. PDF with generated watermark

Attached Image


Dynamic PDFs and Table Display


Now we want to really make FPDF earn its keep. Up to this point, we have only been sending static information to the PDFs being created. Let’s look at how to integrate a PDF document with database information drawn out by a query request. We will display that information in a nicely formatted table on the PDF itself, thus making the PDF dynamic in its content. The following code listing is a little long, but it is well commented and the highlights are discussed in the subsequent paragraphs:


require("&#46;&#46;/&#46;&#46;/fpdf/fpdf.php");



class PDF extends FPDF {



	function BuildTable($header,$data) {

 	//Colors, line width and bold font

 	$this->SetFillColor(255,0,0);

 	$this->SetTextColor(255);

 	$this->SetDrawColor(128,0,0);

 	$this->SetLineWidth(.3);

 	$this->SetFont('','B');

 	//Header

 	// make an array for the column widths

 	$w=array(85,40,15);

 	// send the headers to the PDF document

 	for($i=0;$i<count($header);$i++)

 	$this->Cell($w[$i],7,$header[$i],1,0,'C',1);

 	$this->Ln();

 	//Color and font restoration

 	$this->SetFillColor(175);

 	$this->SetTextColor(0);

 	$this->SetFont('');



 	//now spool out the data from the $data array

 	$fill=true; // used to alternate row color backgrounds

 	foreach($data as $row)

 	{

 	$this->Cell($w[0],6,$row[0],'LR',0,'L',$fill);

 	// set colors to show a URL style link

 	$this->SetTextColor(0,0,255);

 	$this->SetFont('', 'U');

 	$this->Cell($w[1],6,$row[1],'LR',0,'L',$fill, 'http://www.oreilly.com');

 	// restore normal color settings

 	$this->SetTextColor(0);

 	$this->SetFont('');

 	$this->Cell($w[2],6,$row[2],'LR',0,'C',$fill);



 	$this->Ln();

 	// flips from true to false and vise versa

 	$fill =! $fill;

 	}

 	$this->Cell(array_sum($w),0,'','T');

	}

}



//connect to database

$connection = mysql_connect("localhost","user", "password");

$db = "library";

mysql_select_db($db, $connection)

	or die( "Could not open $db database");





$sql = 'SELECT * FROM books ORDER BY pub_year';

$result = mysql_query($sql, $connection)

	or die( "Could not execute sql: $sql");



// build the data array from the database records.

While($row = mysql_fetch_array($result)) {

	$data[] = array($row['title'], $row['ISBN'], $row['pub_year'] );

}



// start and build the PDF document

$pdf = new PDF();



//Column titles

$header=array('Book Title','ISBN','Year');



$pdf->SetFont('Arial','',14);

$pdf->AddPage();

// call the table creation method

$pdf->BuildTable($header,$data);

$pdf->Output();

In this code, we use the database connection and build two arrays to send to the BuildTable() custom method of this extended class. Inside the BuildTable() method, we set colors and font attributes for the table header, then send out the headers based on the first array passed in. An array called $w (for width) sets the column widths and is used in the calls to the cell() methods.


After the table header is sent out, we use the $data array that contains the database information and walk through that array with a foreach loop. Notice here that the cell() method uses LR for its border parameter. This refers to borders on the left and right of the cell in question, thus effectively adding the sides to the table rows. We also add a URL link to the second column just to show that it can be done in connection with the table row construction. Finally, we use a $fill variable to flip back and forth so that the background color will alternate as the table is constructed row by row.


The final call to the cell() method in this BuildTable() method draws the bottom of the table and closes off the columns.


The result of executing this code in a browser is shown in Figure 8.11.


Figure 8.11. Table data taken from MySQL placed in a dynamic PDF


Attached Image

PHP: The Good Parts

Learn more about this topic from PHP: The Good Parts.

Get past all the hype about PHP and dig into the real power of the language. This book explores the most useful features of PHP and how they can speed up the web development process, and explains why the most commonly used PHP elements are often misused or misapplied. You'll learn which parts add strength to object-oriented programming, and how to use certain features to integrate your application with databases.

See what you'll learn


Tags:
0 Subscribe


3 Replies

0
  DarkFlib's Photo
Posted Jan 24 2011 05:19 PM

While I have used FPDF (and pdflib) in a number of projects in the past, I have recently found the Zend_PDF class which is part of the Zend Framework.

It can be used completely separately from the rest of the framework, and supports many features that are only supported in the commercial version of pdflib such as page importing and process colour (CMYK) colourspaces.

Docs are here: http://framework.zen...n/zend.pdf.html
Sysdom.com - Monitoring and Support Services (Launching Feb 2011)
-1
  Hennig's Photo
Posted Sep 17 2013 01:17 AM

Thnaks, that is a famous supposition.. I permit further hitch while.. I hold pdfs in my tangle roster that I dont truly demand to hide nevertheless I do demand to paper writing service users who accessed the pdfs.... I dont conceive it is imaginable to cognomen a php list as ending in .pdf furthermore wield your aloft knack to settle my complication.
0
  pgra_3245's Photo
Posted Apr 03 2014 06:19 PM

what it's mean?
Warning: require(fpdf.php) [function.require]: failed to open stream: No such file or directory in C:\wamp\www\maba\registrar\generate.php on line 2

Fatal error: require() [function.require]: Failed opening required 'fpdf.php' (include_path='.;C:\php5\pear') in C:\wamp\www\maba\registrar\generate.php on line 2