Thursday, October 8, 2009

Tables using FPDF Library

Hi!
This time we'll speak about the exportation of tables in pdf format. I needed a way to print some dockets regarding persons data. This dockets have to be printed using special paper, that is that paper which already have the dockets ready and cut. So the goal of my work was to found a way to generate a table with perfectly equal cells of a given width and height. These cells must contain some text, which is not fixed, but may contain different informations:
Surname - name, Company (optional), Address (may stay on 2 lines), CAP City.
Why didn't I use html and the javascript print function to make it work? Clear, because I was glad to study something new and because printing to a pdf file is a more professional way to solve this problem.
One difficulty was represented by the variability of the text, so I assumed that each cell could have a maximum of 5 lines:
- surname and name
- company (optional)
- address 1 line
- address 2 line (if needed)
- cap and city
So there can be dockets containing text between 3 an 5 lines.
I decided to use the fpdf library which is free of course. You may download it from here (I used the last version 1.6).
Now this library has many useful functions but I extended it in order to get my desired table.
So here is my class and below how to instanciate and use it. Good reading.
<?php
/*
 * CLASS TblFPDF
 * 08/09/2009
 * written by abidibo <abidibo@gmail.com>
 * Copyright: FUCK COPYRIGHT, NO LICENCE TAKE THIS CODE AND DO WHAT YOU WANT
 *
 * Description
 *     This class allows to create a pdf table made by cells all of the same dimensions
 *     (width and height) and containing some text. This text must follow some rules, as
 *     this class was written in order to print some dockets, and dockets hava a fixed
 *     structure. More informations later.
 *
 * Parameters of __construct function
 *      (int) $nRows : number or rows for document page
 *      (int) $nCols : number of columns
 *      (float) $cellWidth : width of every single cell in mm
 *      (float) $cellHeight : height of every single cell in mm
 *      (mix) $cellBorder : cell border property
 *               0 -> no border
 *               1 -> border
 *               string containing one ore more characters:
 *               L -> left border
 *               T -> top border
 *               R -> right border
 *               B -> bottom border
 *
 * Parameters of render function
 *     (array) $textArray : an array where each element represent the string
 *                          to insert in a cell. Break lines represented by \n
 *     (int) $cellLines : the maximum number of lines inside a cell.
 *
 *     The $textArray[$i] text foreach $i has to stay inside the cell, that is has to have
 *     a maximum of ($cellLines-1) break lines or less if the text between two
 *     break lines is so long that can't stay in a single line of width $cellWidth.
 *     That's a very important condition!
 *
 */

// include fpdf library and set the constant it used to charge font folder
// font folder is given with the library
define('FPDF_FONTPATH',dirname(__FILE__).'/font/');
require('fpdf.php');

class TblFPDF extends FPDF {
    function __construct($nRows, $nCols, $cellWidth, $cellHeight, $cellBorder) {
 
        parent::__construct();
        $this->_nrows = $nRows;           // rows for page
        $this->_ncols = $nCols;           // columns
        $this->_cellw = $cellWidth;
        $this->_cellh = $cellHeight;
        $this->_cellb = $cellBorder;
    }

    public function render($textArray, $cellLines) {

        $count = 0;
        reset($textArray);
        $totCell = count($textArray);
        $totPages = ceil($totCell/($this->_nrows*$this->_ncols));
      
        for($p=0;$p<$totPages;$p++) {
            $this->AddPage();
            for($r=0;$r<$this->_nrows;$r++) {
                for($c=0;$c<$this->_ncols;$c++) {
                    if($count>=$totCell) break;       // all cells has been already printed
                    $this->setXY($c*$this->_cellw, $r*$this->_cellh);    // set x and y position of the top left corner of the new cell
                    $this->renderCell(current($textArray), $cellLines);
                    next($textArray);
                    $count++;
                }
            }
        }
    }

    private function renderCell($text, $cellLines) {

        $totLines = 0;
        $text_lines = explode("\n", $text);
        for($i=0;$i<count($text_lines);$i++) {
            $totLines++;
            if($this->getStringWidth($text_lines[$i])>=$this->_cellw-2.6) $totLines++;
        }
        if($totLines<$cellLines) $text .= "\n";
        for($i=0;$i<($cellLines-$totLines);$i++) $text .= "\n";

        $this->MultiCell($this->_cellw, ($this->_cellh/$cellLines), $text, $this->_cellb, "L");
    }
}
?>

So this was the class I used. It's well commented, but let's see how to use it

<?php

require_once(class.TblFPDF.php);

$textArray = array(
                          0=>"Name Surname\nCompany\nAddress\nCAP City",
                          1=>"Gino Pinotto\nTalisker spa\n5th 23/q\n10384 New York"
                      );
$pdfDoc = new TblFPDF(8,3,70,37,1);
$pdfDoc->render($textArray);
$pdfDoc->Output();
exit();

?>

In this example the output will be a pdf document with two dockets drawned starting from the point (0,0) of the page, containing the informations included in $textArray. If many text elements are given the full page will have 8 rows for 3 columns, each cell will have a width of 7 cm and an height of 3.7 cm, with a black border. The text inside the cell must occupy not more than 5 lines, that's the only recomendation.
Hope is useful.
Bye!
PS. Yesterday Italy's top court stripped Mr. Berlusconi of his legal immnuity (I gloat for this), hoping justice may be done I'll have a great party!