The PDF format can be a handy way to distribute documents to your visitors. A PDF document is self-contained, looks the same on any PDF reader, and is easy to print. PDFs are often used for reports, brochures, manuals, invoices, product data sheets, and lots more.
Often it's useful to be able to create PDF documents dynamically from within a PHP script. For example, you can produce a custom PDF report based on a user's preferences and include up-to-the-minute data.
In this tutorial I'll walk you through the process of creating a nice-looking, 2-page PDF document using PHP. You'll use the freely-available FPDF library to handle the nitty-gritty of PDF creation.
Here's what your PDF will look like (click to view the finished PDF):
=>info
=>If you want to try out the finished PHP script, you can grab the entire code at the end of the article.
Installing FPDF
To use FPDF, you first need to install the FPDF files on your website. To do this, download the FPDF archive file and extract it to a folder within your website. Call the folder fpdf.
Starting the PHP script
Now that you've installed FPDF, you can start writing your PHP script to produce the PDF report. Create a file called report.php in the same place that you saved your fpdf folder, and open the file in a text editor.
The first thing to do is include the FPDF library so that you can use it. The library is called fpdf.php, and it's inside the fpdf folder that you extracted earlier:
require_once( "fpdf/fpdf.php" );
Now add some variables to configure the report:
// Begin configuration
$textColour = array( 0, 0, 0 );
$headerColour = array( 100, 100, 100 );
$tableHeaderTopTextColour = array( 255, 255, 255 );
$tableHeaderTopFillColour = array( 125, 152, 179 );
$tableHeaderTopProductTextColour = array( 0, 0, 0 );
$tableHeaderTopProductFillColour = array( 143, 173, 204 );
$tableHeaderLeftTextColour = array( 99, 42, 57 );
$tableHeaderLeftFillColour = array( 184, 207, 229 );
$tableBorderColour = array( 50, 50, 50 );
$tableRowFillColour = array( 213, 170, 170 );
$reportName = "2009 Widget Sales Report";
$reportNameYPos = 160;
$logoFile = "widget-company-logo.png";
$logoXPos = 50;
$logoYPos = 108;
$logoWidth = 110;
$columnLabels = array( "Q1", "Q2", "Q3", "Q4" );
$rowLabels = array( "SupaWidget", "WonderWidget", "MegaWidget", "HyperWidget" );
$chartXPos = 20;
$chartYPos = 250;
$chartWidth = 160;
$chartHeight = 80;
$chartXLabel = "Product";
$chartYLabel = "2009 Sales";
$chartYStep = 20000;
$chartColours = array(
array( 255, 100, 100 ),
array( 100, 255, 100 ),
array( 100, 100, 255 ),
array( 255, 255, 100 ),
);
$data = array(
array( 9940, 10100, 9490, 11730 ),
array( 19310, 21140, 20560, 22590 ),
array( 25110, 26260, 25210, 28370 ),
array( 27650, 24550, 30040, 31980 ),
);
// End configuration
These variables make it easy to tweak your report by keeping all the key configuration data at the top of the file. The variables include:
Various colours used in the report.
Each colour is specified as a 3-element array containing red, green and blue values (in the range 0-255).
The report title ("2009 Widget Sales Report") and position.
The URL and dimensions of the company logo image. You'll include this image in the title page of the report.
The row and column labels for the report data. You'll use these when displaying the table and chart in the report.
Configuration settings for the chart. These include the chart position, dimensions, axis labels, and the step value to use for the Y-axis scale.
The colours to use for the chart bars. As with the other report colours, these are specified as 3-element arrays. There are 4 colours: 1 for each bar in the chart.
The report data. This is a 2-dimensional array containing 4 rows of quarterly sales figures, 1 row per product.
Creating the title page
Now that you've set up the report configuration, you're ready to start building the PDF. First, you'll create the title page for the report. This consists of the company logo and the report name, centred in the page.
Creating the FPDF object
The first thing you need to do is create a new FPDF object to hold the PDF data. The FPDF constructor accepts 3 optional arguments, as follows:
The page orientation. Use 'P' for portrait, or 'L' for landscape. The default is 'P'.
The units to use for the page measurements. Use 'pt', 'mm', 'cm', or 'in'. The default is 'mm'.
The page format. Possible values include 'A3', 'A4', 'A5', 'Letter', and 'Legal'. Or you can specify a custom width and height with a 2-element array. The default value is 'A4'.
For this example, use a portrait orientation, millimetres for units, and A4 format:
/**
Create the title page
**/
$pdf = new FPDF( 'P', 'mm', 'A4' );
Setting the text colour
Now set the colour to use for text in the page. You do this by calling the FPDF SetTextColor() method, passing in the red, green and blue values of the colour to use (each value should be in the range 0-255). Use the colour values in the $textColour array that you created in the configuration section earlier:
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
Creating a page
Now you're ready to create a new blank page in the PDF by calling FPDF's AddPage() method:
$pdf->AddPage();
Adding the logo image
FPDF makes it really easy to insert images in the page. Just call the Image() method, passing in the following arguments:
The path of the image file. This can be an absolute path, or relative to the PHP script. You can also use a URL.
The X and Y position of the top left corner of the image in the PDF. If you omit these then the current position is used.
The width and height of the image in the PDF. If you omit both values then the original image dimensions are used (at 72 DPI). If you omit 1 value then the other value is calculated automatically.
The image type. Allowed values include 'JPG', 'JPEG', 'PNG' and 'GIF' (upper- or lower-case). If you omit this value then FPDF guesses the image format from the filename extension.
A URL to link the image to. This is optional — if you supply a URL then the image becomes a clickable link.
All measurements, such as X and Y positions, widths and heights, use the units you specified when you created the PDF (mm in this case).
You can create your own logo image (make sure it's 300 DPI) or download my example image. Save your logo image in the same folder as your PHP script, then insert the image in the PDF as follows:
// Logo
$pdf->Image( $logoFile, $logoXPos, $logoYPos, $logoWidth );
Setting the font
FPDF lets you choose the font face, style and size to use for text in the PDF. To do this, you call the SetFont() method, which takes the following arguments:
The font family. You can use any of the following standard family names: 'Courier', 'Helvetica', 'Arial', 'Times', 'Symbol', or 'ZapfDingbats'.
The font style. Options include: '' (regular), 'B' (bold), 'I' (italic), and 'U' (underline). You can combine these — for example, 'BU' for bold, underlined text.
The font size. You specify this in points (it defaults to 12 points).
For the report name on the title page, use an Arial Bold 24-point font:
// Report Name
$pdf->SetFont( 'Arial', 'B', 24 );
As well as using the standard fonts, you can import any TrueType or Type 1 font using the AddFont() method. See the manual on the FPDF website for details.
Adding some text
You're now ready to add the report name. FPDF objects have a concept of "the current position", which is where the next piece of text or other element will be inserted. Since you want the report name to appear just after halfway down the page, you first need to move the current position down to this point, which is 160mm from the top of the page (this is stored in the $reportNameYPos configuration variable). To do this, use FPDF's Ln() method, which adds a line break with an optional height value:
$pdf->Ln( $reportNameYPos );
If you don't specify a height for the line break then the height of the last printed cell is used.
Now add the report name. There are a few different ways that you can add text using FPDF. In this case, you'll use the Cell() method, which, amongst other things, lets you easily centre text.
Cell() takes the following arguments (all of them optional):
The cell width and height. If you omit the width then the cell stretches to the right margin. If you omit the height then it defaults to zero.
The string of text to print. Defaults to ''.
Whether to draw a border around the cell. This can be either a number (0=no border, 1=border), or a string containing 1 or more of the following: 'L' (left), 'T' (top), 'R' (right), and 'B' (bottom). Default: 0.
Where to place the current position after drawing the cell. Values can be 0 (to the right), 1 (to the start of the next line), or 2 (below). Default: 0.
The text alignment. Possible values are 'L' (left align), 'C' (centre), or 'R' (right align). Default: 'L'.
Whether the cell background should be filled with colour. true = filled, false = transparent. Default: false.
A URL to link to. If specified, turns the text cell into a link.
Now, use Cell() to insert the report name and centre it, as follows:
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
Creating a page header and intro text
That's the title page done. Now you'll create the page containing some header text, a heading, and some intro text, followed by a table and chart of sales data.
First the page header. Add a new page, then output the page header, which consists of the report name centred at the top of the page using an Arial Regular 17-point font. Use the $headerColour configuration variable to set the text colour:
$pdf->AddPage();
$pdf->SetTextColor( $headerColour[0], $headerColour[1], $headerColour[2] );
$pdf->SetFont( 'Arial', '', 17 );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
Now for the intro text. First print a heading using the regular text colour and an Arial 20-point font. Since you don't need this text to be centred, you can use the simpler Write() method, which takes the line height, the text to write, and an optional link URL:
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFont( 'Arial', '', 20 );
$pdf->Write( 19, "2009 Was A Good Year" );
Now you can output the intro text itself in 12-point Arial. This consists of a 16mm line break, followed by the first paragraph, a 12mm line break, and the final paragraph. Give each line a line height of 6mm:
$pdf->Ln( 16 );
$pdf->SetFont( 'Arial', '', 12 );
$pdf->Write( 6, "Despite the economic downturn, WidgetCo had a strong year. Sales of the HyperWidget in particular exceeded expectations. The fourth quarter was generally the best performing; this was most likely due to our increased ad spend in Q3." );
$pdf->Ln( 12 );
$pdf->Write( 6, "2010 is expected to see increased sales growth as we expand into other countries." );
The Write() method automatically wraps text when it reaches the right side of the page.
Adding a table of data
Next you'll add a table of sales data below the intro text. First, set the border colour for the table. The SetDrawColor() method sets the colour to use for borders and other lines, so you can use this to set the table cell borders. Then move down 15mm to create a space between the intro text and the table:
$pdf->SetDrawColor( $tableBorderColour[0], $tableBorderColour[1], $tableBorderColour[2] );
$pdf->Ln( 15 );
Creating the table header row
The table header row consists of the "PRODUCT", "Q1, "Q2", "Q3", and "Q4" cells. The "PRODUCT" cell uses different text and background colours to the other header cells.
You already know to call the SetTextColor() method to set the text colour to use. To set the background colour to use, you call SetFillColor(), which takes the same RGB arguments as SetTextColor().
To create table cells you use — you guessed it — the Cell() method, specifying the cell width, height, contents, and alignment. You'll also pass 1 as the 4th argument to set a border, and true as the 7th argument to fill the cell with a background colour.
Here, then, is the code to create the table header row. First you set a bold font, then create the left-aligned "PRODUCT" cell with appropriate text and fill colours. Finally, you set colours for the remaining 4 header cells, then loop through the $columnLabels array to display the cells using centred text:
// Create the table header row
$pdf->SetFont( 'Arial', 'B', 15 );
// "PRODUCT" cell
$pdf->SetTextColor( $tableHeaderTopProductTextColour[0], $tableHeaderTopProductTextColour[1], $tableHeaderTopProductText
Colour[2] );
$pdf->SetFillColor( $tableHeaderTopProductFillColour[0], $tableHeaderTopProductFillColour[1], $tableHeaderTopProductFill
Colour[2] );
$pdf->Cell( 46, 12, " PRODUCT", 1, 0, 'L', true );
// Remaining header cells
$pdf->SetTextColor( $tableHeaderTopTextColour[0], $tableHeaderTopTextColour[1], $tableHeaderTopTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopFillColour[0], $tableHeaderTopFillColour[1], $tableHeaderTopFillColour[2] );
for ( $i=0; $i
}
$pdf->Ln( 12 );
The space character before the word "PRODUCT" in the code helps to pad the word within the table cell so that it isn't hard up against the left edge of the cell. The same trick is used later on with the product names in the left hand column. (Unfortunately there's currently no way to control cell padding with FPDF without extending the class.)
Creating the data rows
The rest of the table consists of 4 rows of sales figures — 1 row for each product — over the 4 quarters. First, set a couple of variables:
// Create the table data rows
$fill = false;
$row = 0;
These variables work as follows:
$fill : Whether a cell should be filled or not. You'll toggle this value every time you've drawn a row to create a striped row effect.
$row : The current row number. This lets you display the appropriate row label for each row as you move through the table.
Now you can loop through the $data array using a foreach loop, printing a row at a time. For each row you create the left header cell containing the product name, and the 4 data cells containing the sales data. Set appropriate text and background colours for each cell as you go.
To display the data cells, use a for loop to move through the 4-element array containing the data, calling the PHP number_format() function to print the sales figure with thousands separators.
After displaying a row, you increment the $row variable, toggle the $fill variable, and use Ln() to move down to the start of the next line, ready to output the next row.
Here's the code for the whole loop:
foreach ( $data as $dataRow ) {
// Create the left header cell
$pdf->SetFont( 'Arial', 'B', 15 );
$pdf->SetTextColor( $tableHeaderLeftTextColour[0], $tableHeaderLeftTextColour[1], $tableHeaderLeftTextColour[2] );
$pdf->SetFillColor( $tableHeaderLeftFillColour[0], $tableHeaderLeftFillColour[1], $tableHeaderLeftFillColour[2] );
$pdf->Cell( 46, 12, " " . $rowLabels[$row], 1, 0, 'L', $fill );
// Create the data cells
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFillColor( $tableRowFillColour[0], $tableRowFillColour[1], $tableRowFillColour[2] );
$pdf->SetFont( 'Arial', '', 15 );
for ( $i=0; $i
$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}
Creating a bar chart
The last element of the page is a bar chart showing the total sales figures for the 4 products over the whole year.
Calculating scales and bar width
The first thing to do is compute the scales for the X and Y axes. For the X scale this is simply the number of products to display divided by the desired chart width (subtracting some millimetres to allow for space to the left of the bars):
/***
Create the chart
***/
// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );
To compute the Y scale, you need to find the total sales figure for each product, then determine the highest sales figure across all the products. You can then divide this by the desired chart height to get the Y scale:
// Compute the Y scale
$maxTotal = 0;
foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}
$yScale = $maxTotal / $chartHeight;
Now that you know the X scale, you can work out the width (in mm) of each bar in the chart. This is the inverse of the X scale value, reduced by a factor of 1.5 to allow some space between each bar:
// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;
Adding the axis lines and labels
So far, so good. Now you can add the X and Y axis lines, data labels, and axis labels. Use Arial 10-point for the data labels.
To create lines in FDPF, you use the Line() method, which accepts 4 arguments: the X and Y co-ordinates of the start of the line, and the X and Y co-ordinates of the end of the line.
For the X axis, draw a horizontal line along the bottom of the chart, allowing 30mm for the Y-axis labels on the left. Then loop through each product name in the $rowLabels array, printing the product name as a text cell at the appropriate point:
// Add the axes:
$pdf->SetFont( 'Arial', '', 10 );
// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );
for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}
The SetXY() method lets you set the current position to a specific location on the page.
For the Y axis, draw a vertical line up the left side of the chart, again allowing 30mm for the Y-axis labels. Extend the line 8mm above the desired chart height to make room for the axis label later on. Then loop from zero up to the highest bar value, $maxTotal, that you calculated earlier. Jump in steps of $chartYStep (20,000) dollars. At each step, display the current value (right-aligned) and a short tick mark:
// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );
for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}
Now you can add the axis labels. Use Arial Bold 12-point. Place the X-axis label below the data labels, and the Y-axis label at the top of the Y axis:
// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );
Drawing the data bars
The last stage of creating the chart is to draw the bars themselves. To draw a bar you can use FPDF's Rect() method, which draws a rectangle using the following arguments:
The X and Y co-ordinates of the upper left corner of the rectangle.
The width and height of the rectangle.
The rectangle style. This can be 'D' or '' (draw a border), 'F' (fill with the current fill colour), or 'DF' / 'FD' (draw and fill).
Now draw the bars. First, Set a variable, $xPos, to track the current bar X position; set it 40mm to the right of the chart's start position to allow for the Y-axis labels and a gap at the start of the bars. Then create a variable, $bar, to hold the current bar number; you'll use this to work out which fill colour to use for each bar:
// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;
Now loop through the $data array, totalling up the value in each row and drawing a bar from the X axis up to that value, scaled using $yScale. Colour each bar differently by using the $bar counter and the colour values in the $chartColours array. After you've drawn each bar, move the X position along to the start of the next bar, increment the $bar counter, and continue the loop:
foreach ( $data as $dataRow ) {
// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}
The above code uses the PHP modulus (%) operator to repeat the bar colours if the number of bars happens to be greater than the number of elements in the $chartColours array.
Sending the PDF to the browser
Your PDF is finished! The only thing left to do is send the PDF to the browser so that the user can view or download it.
To do this, you call FPDF's Output() method to send the PDF data. This accepts 2 arguments: the suggested filename for the PDF, and a destination flag. This flag can have any of the following values:
I : Displays the PDF inline if supported by the browser, otherwise it's downloaded.
D: Forces the PDF to be downloaded.
F: Saves the file to a folder on the server.
S: Returns the PDF data as a string.
For this example, use the I option to display the PDF inline if possible:
/***
Serve the PDF
***/
$pdf->Output( "report.pdf", "I" );
?>
Output() automatically sends an HTTP "Content-type: application/pdf" header, which tells the browser to expect a PDF document.
You're now ready to test your script. Open your browser and visit the script's URL — for example, www.example.com/report.php. You should see the PDF appear in your browser window. Alternatively you might see a dialog appear that lets you save the PDF to your hard drive. You can then open up the PDF in your PDF viewer, such as Acrobat Reader or Preview.
That's it! You've now created a PDF document on the fly using nothing but PHP and FPDF. Good work!
The complete script
Here's the complete PHP script for you to copy, paste, and play with:
SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->AddPage();
// Logo
$pdf->Image( $logoFile, $logoXPos, $logoYPos, $logoWidth );
// Report Name
$pdf->SetFont( 'Arial', 'B', 24 );
$pdf->Ln( $reportNameYPos );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
/**
Create the page header, main heading, and intro text
**/
$pdf->AddPage();
$pdf->SetTextColor( $headerColour[0], $headerColour[1], $headerColour[2] );
$pdf->SetFont( 'Arial', '', 17 );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFont( 'Arial', '', 20 );
$pdf->Write( 19, "2009 Was A Good Year" );
$pdf->Ln( 16 );
$pdf->SetFont( 'Arial', '', 12 );
$pdf->Write( 6, "Despite the economic downturn, WidgetCo had a strong year. Sales of the HyperWidget in particular exceeded expectations. The fourth quarter was generally the best performing; this was most likely due to our increased ad spend in Q3." );
$pdf->Ln( 12 );
$pdf->Write( 6, "2010 is expected to see increased sales growth as we expand into other countries." );
/**
Create the table
**/
$pdf->SetDrawColor( $tableBorderColour[0], $tableBorderColour[1], $tableBorderColour[2] );
$pdf->Ln( 15 );
// Create the table header row
$pdf->SetFont( 'Arial', 'B', 15 );
// "PRODUCT" cell
$pdf->SetTextColor( $tableHeaderTopProductTextColour[0], $tableHeaderTopProductTextColour[1], $tableHeaderTopProductTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopProductFillColour[0], $tableHeaderTopProductFillColour[1], $tableHeaderTopProductFillColour[2] );
$pdf->Cell( 46, 12, " PRODUCT", 1, 0, 'L', true );
// Remaining header cells
$pdf->SetTextColor( $tableHeaderTopTextColour[0], $tableHeaderTopTextColour[1], $tableHeaderTopTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopFillColour[0], $tableHeaderTopFillColour[1], $tableHeaderTopFillColour[2] );
for ( $i=0; $i
}
$pdf->Ln( 12 );
// Create the table data rows
$fill = false;
$row = 0;
foreach ( $data as $dataRow ) {
// Create the left header cell
$pdf->SetFont( 'Arial', 'B', 15 );
$pdf->SetTextColor( $tableHeaderLeftTextColour[0], $tableHeaderLeftTextColour[1], $tableHeaderLeftTextColour[2] );
$pdf->SetFillColor( $tableHeaderLeftFillColour[0], $tableHeaderLeftFillColour[1], $tableHeaderLeftFillColour[2] );
$pdf->Cell( 46, 12, " " . $rowLabels[$row], 1, 0, 'L', $fill );
// Create the data cells
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFillColor( $tableRowFillColour[0], $tableRowFillColour[1], $tableRowFillColour[2] );
$pdf->SetFont( 'Arial', '', 15 );
for ( $i=0; $i
}
$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}
/***
Create the chart
***/
// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );
// Compute the Y scale
$maxTotal = 0;
foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}
$yScale = $maxTotal / $chartHeight;
// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;
// Add the axes:
$pdf->SetFont( 'Arial', '', 10 );
// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );
for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}
// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );
for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}
// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );
// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;
foreach ( $data as $dataRow ) {
// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}
/***
Serve the PDF
***/
$pdf->Output( "report.pdf", "I" );
?>
Summary
In this tutorial you've learned how to use PHP with the FPDF library to produce a good-looking PDF report. Along the way you've looked at some useful methods of the FPDF library, and seen some techniques for creating tables and charts using FPDF.
There's a lot more to FPDF, such as the ability to create headers and footers, use automatic page breaks, and more. Find out more by reading the online manual at the FPDF website.
Have fun!
--------------------------------------------
Tutorial by Matt Doyle | Level: Intermediate | Published on 26 February 2010
Categories:
Web Development > PHP
}
$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}
Creating a bar chart
The last element of the page is a bar chart showing the total sales figures for the 4 products over the whole year.
Calculating scales and bar width
The first thing to do is compute the scales for the X and Y axes. For the X scale this is simply the number of products to display divided by the desired chart width (subtracting some millimetres to allow for space to the left of the bars):
/***
Create the chart
***/
// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );
To compute the Y scale, you need to find the total sales figure for each product, then determine the highest sales figure across all the products. You can then divide this by the desired chart height to get the Y scale:
// Compute the Y scale
$maxTotal = 0;
foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}
$yScale = $maxTotal / $chartHeight;
Now that you know the X scale, you can work out the width (in mm) of each bar in the chart. This is the inverse of the X scale value, reduced by a factor of 1.5 to allow some space between each bar:
// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;
Adding the axis lines and labels
So far, so good. Now you can add the X and Y axis lines, data labels, and axis labels. Use Arial 10-point for the data labels.
To create lines in FDPF, you use the Line() method, which accepts 4 arguments: the X and Y co-ordinates of the start of the line, and the X and Y co-ordinates of the end of the line.
For the X axis, draw a horizontal line along the bottom of the chart, allowing 30mm for the Y-axis labels on the left. Then loop through each product name in the $rowLabels array, printing the product name as a text cell at the appropriate point:
// Add the axes:
$pdf->SetFont( 'Arial', '', 10 );
// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );
for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}
The SetXY() method lets you set the current position to a specific location on the page.
For the Y axis, draw a vertical line up the left side of the chart, again allowing 30mm for the Y-axis labels. Extend the line 8mm above the desired chart height to make room for the axis label later on. Then loop from zero up to the highest bar value, $maxTotal, that you calculated earlier. Jump in steps of $chartYStep (20,000) dollars. At each step, display the current value (right-aligned) and a short tick mark:
// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );
for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}
Now you can add the axis labels. Use Arial Bold 12-point. Place the X-axis label below the data labels, and the Y-axis label at the top of the Y axis:
// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );
Drawing the data bars
The last stage of creating the chart is to draw the bars themselves. To draw a bar you can use FPDF's Rect() method, which draws a rectangle using the following arguments:
The X and Y co-ordinates of the upper left corner of the rectangle.
The width and height of the rectangle.
The rectangle style. This can be 'D' or '' (draw a border), 'F' (fill with the current fill colour), or 'DF' / 'FD' (draw and fill).
Now draw the bars. First, Set a variable, $xPos, to track the current bar X position; set it 40mm to the right of the chart's start position to allow for the Y-axis labels and a gap at the start of the bars. Then create a variable, $bar, to hold the current bar number; you'll use this to work out which fill colour to use for each bar:
// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;
Now loop through the $data array, totalling up the value in each row and drawing a bar from the X axis up to that value, scaled using $yScale. Colour each bar differently by using the $bar counter and the colour values in the $chartColours array. After you've drawn each bar, move the X position along to the start of the next bar, increment the $bar counter, and continue the loop:
foreach ( $data as $dataRow ) {
// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}
The above code uses the PHP modulus (%) operator to repeat the bar colours if the number of bars happens to be greater than the number of elements in the $chartColours array.
Sending the PDF to the browser
Your PDF is finished! The only thing left to do is send the PDF to the browser so that the user can view or download it.
To do this, you call FPDF's Output() method to send the PDF data. This accepts 2 arguments: the suggested filename for the PDF, and a destination flag. This flag can have any of the following values:
I : Displays the PDF inline if supported by the browser, otherwise it's downloaded.
D: Forces the PDF to be downloaded.
F: Saves the file to a folder on the server.
S: Returns the PDF data as a string.
For this example, use the I option to display the PDF inline if possible:
/***
Serve the PDF
***/
$pdf->Output( "report.pdf", "I" );
?>
Output() automatically sends an HTTP "Content-type: application/pdf" header, which tells the browser to expect a PDF document.
You're now ready to test your script. Open your browser and visit the script's URL — for example, www.example.com/report.php. You should see the PDF appear in your browser window. Alternatively you might see a dialog appear that lets you save the PDF to your hard drive. You can then open up the PDF in your PDF viewer, such as Acrobat Reader or Preview.
That's it! You've now created a PDF document on the fly using nothing but PHP and FPDF. Good work!
The complete script
Here's the complete PHP script for you to copy, paste, and play with:
SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->AddPage();
// Logo
$pdf->Image( $logoFile, $logoXPos, $logoYPos, $logoWidth );
// Report Name
$pdf->SetFont( 'Arial', 'B', 24 );
$pdf->Ln( $reportNameYPos );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
/**
Create the page header, main heading, and intro text
**/
$pdf->AddPage();
$pdf->SetTextColor( $headerColour[0], $headerColour[1], $headerColour[2] );
$pdf->SetFont( 'Arial', '', 17 );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFont( 'Arial', '', 20 );
$pdf->Write( 19, "2009 Was A Good Year" );
$pdf->Ln( 16 );
$pdf->SetFont( 'Arial', '', 12 );
$pdf->Write( 6, "Despite the economic downturn, WidgetCo had a strong year. Sales of the HyperWidget in particular exceeded expectations. The fourth quarter was generally the best performing; this was most likely due to our increased ad spend in Q3." );
$pdf->Ln( 12 );
$pdf->Write( 6, "2010 is expected to see increased sales growth as we expand into other countries." );
/**
Create the table
**/
$pdf->SetDrawColor( $tableBorderColour[0], $tableBorderColour[1], $tableBorderColour[2] );
$pdf->Ln( 15 );
// Create the table header row
$pdf->SetFont( 'Arial', 'B', 15 );
// "PRODUCT" cell
$pdf->SetTextColor( $tableHeaderTopProductTextColour[0], $tableHeaderTopProductTextColour[1], $tableHeaderTopProductTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopProductFillColour[0], $tableHeaderTopProductFillColour[1], $tableHeaderTopProductFillColour[2] );
$pdf->Cell( 46, 12, " PRODUCT", 1, 0, 'L', true );
// Remaining header cells
$pdf->SetTextColor( $tableHeaderTopTextColour[0], $tableHeaderTopTextColour[1], $tableHeaderTopTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopFillColour[0], $tableHeaderTopFillColour[1], $tableHeaderTopFillColour[2] );
for ( $i=0; $i
}
$pdf->Ln( 12 );
// Create the table data rows
$fill = false;
$row = 0;
foreach ( $data as $dataRow ) {
// Create the left header cell
$pdf->SetFont( 'Arial', 'B', 15 );
$pdf->SetTextColor( $tableHeaderLeftTextColour[0], $tableHeaderLeftTextColour[1], $tableHeaderLeftTextColour[2] );
$pdf->SetFillColor( $tableHeaderLeftFillColour[0], $tableHeaderLeftFillColour[1], $tableHeaderLeftFillColour[2] );
$pdf->Cell( 46, 12, " " . $rowLabels[$row], 1, 0, 'L', $fill );
// Create the data cells
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFillColor( $tableRowFillColour[0], $tableRowFillColour[1], $tableRowFillColour[2] );
$pdf->SetFont( 'Arial', '', 15 );
for ( $i=0; $i
}
$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}
/***
Create the chart
***/
// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );
// Compute the Y scale
$maxTotal = 0;
foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}
$yScale = $maxTotal / $chartHeight;
// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;
// Add the axes:
$pdf->SetFont( 'Arial', '', 10 );
// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );
for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}
// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );
for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}
// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );
// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;
foreach ( $data as $dataRow ) {
// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}
/***
Serve the PDF
***/
$pdf->Output( "report.pdf", "I" );
?>
Summary
In this tutorial you've learned how to use PHP with the FPDF library to produce a good-looking PDF report. Along the way you've looked at some useful methods of the FPDF library, and seen some techniques for creating tables and charts using FPDF.
There's a lot more to FPDF, such as the ability to create headers and footers, use automatic page breaks, and more. Find out more by reading the online manual at the FPDF website.
Have fun!
--------------------------------------------
Tutorial by Matt Doyle | Level: Intermediate | Published on 26 February 2010
Categories:
Web Development > PHP
ไม่มีความคิดเห็น:
แสดงความคิดเห็น