FPDF is a library written for PHP 4.x by Oliver Pathey. It’s a wonderful piece of work and I have nothing but the highest respect for the original author. That said, it was written for PHP 4.x. Last version number from the site is 1.6 and was updated about 3 years ago. It’s been used frequently enough by the users here, about once or month or so I see a question relating to it. And after 7 months of heavy use some of its API shortcomings have really gotten onto my nerves. So I’ve resolved to build a better version of it compatible with PHP 5.3.
Before I do that though I want to discuss API goals. Now, I’d put this in the PHP Application design forum, but that forum is - frankly - dead. Last post I made there a month ago is still the 4th thread from the top - and in my opinion its time to just merge that forum into this one and be done with it.
While I’m doing this mostly for myself, I will be releasing this under an open source license (the current one is has a license that has no copyleft protections which I view as a problem). That means the API will be used by others, and it therefore needs to make sense. Since this is a major version change I don’t feel obligated to make sure it’s backwards compatible. I may end up renaming the project though.
The reason for the rewriting is because the current API, frankly, sucks. This is the argument list of the Cell command
Cell(float w [, float h [, string txt [, mixed border [, int ln [, string align [, boolean fill [, mixed link]]]]]]])
So even if you don’t need to define a width or height and just want to write some text, you got to repeat those arguments. Making a link is a headache since you must traverse 3 unneeded arguments to get to it. It works, but it’s a pain.
The library is also emphatically not very extensible because it’s monolithic. It’s really just one 1700 line class. You can extend it, but it’s trickier than it needs to be.
Ok, enough ranting, this is what I plan.
Primary Project Goals
[list][]Minimize external code through use of logical defaults.
[]Rely on chaining for most property setting to allow for self-documenting code.
[]Allow PDF elements to be generated out of sequence and modified arbitrarily up until output time.
[]Modularize code into as many classes as necessary rather than presenting one monolithic class.
[]Allow positioning of elements to be absolute to the page, or relative to other objects on the page.
[]Have a little fun.
[/list]
First, since we are going for PHP 5.3 floor this will be a namespaced library composed of multiple classes. Each class corresponds to a document element, or the document itself. Another reason for the namespacing is to allow these classnames to be generic and intuitive without fear of collision with other products out there. The current planned classes are:
- Document: This is the core object, which represents a single PDF document. Documents will be able to merge, and usually you get the other objects from the document.
- Section: Documents break up into sections, which share common margins, headers, body, footers.
- Page: A single sheet of paper within the document
- Header: Header objects can be attached to documents to be document wide, to sections to be section wide, and to pages to be page specific.
- Body: A section or document body may span multiple pages, might be broken into columns. Bodies attach to sections or documents.
- Footer: As with headers, footers can be attached to documents, sections or pages.
- Cell: A single text cell, which is the fundamental unit of PDFs in the system.
- Image: An image object
- Font: A font definition object.
- Drawing: A vector calculated drawing, such as a circle or elipse - as opposed to Image objects which are raster based.
The largest change would be making use of chaining. Most of these objects will return themselves. Here’s the tutorial minimal example from the site.
require('fpdf.php');
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
$pdf->Output();
Under the new API it would be
require('fpdf.php');
$pdf = new FPDF\\Document();
$pdf->createCell('Hello World')->face('Arial')->size(16)->bold();
$pdf->Output();
Internally, asking the document to create a cell when it has no sections or pages will force a default section and page to be created. This doesn’t have to be explicitly called. The cell height and width doesn’t need to be defined in either version to get the text to appear properly, but because of the current API design you have to pass something in for the first example.
Moving onto something a bit more complex, the second tutorail - a 2 page document with header and footer. The current FPDF library requires extension of the base class.
require('fpdf.php');
class PDF extends FPDF
{
// Page header
function Header()
{
// Logo
$this->Image('logo.png',10,6,30);
// Arial bold 15
$this->SetFont('Arial','B',15);
// Move to the right
$this->Cell(80);
// Title
$this->Cell(30,10,'Title',1,0,'C');
// Line break
$this->Ln(20);
}
// Page footer
function Footer()
{
// Position at 1.5 cm from bottom
$this->SetY(-15);
// Arial italic 8
$this->SetFont('Arial','I',8);
// Page number
$this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C');
}
}
// Instanciation of inherited class
$pdf = new PDF();
$pdf->AliasNbPages();
$pdf->AddPage();
$pdf->SetFont('Times','',12);
for($i=1;$i<=40;$i++)
$pdf->Cell(0,10,'Printing line number '.$i,0,1);
$pdf->Output();
I think PHP 5.3 and some chaining will allow for better…
require('fpdf.php');
$pdf = new FPDF\\Document();
$header = $pdf->createHeader()
$header->createImage('logo.png')->width(10)->height(6)->xPos(30);
$header->createCell('Title')->face('Arial')->bold()->center()->border()->marginLeft(80);
$header->marginBottom(20);
$pdf->createFooter(function() use ($self) {
$self->createCell( 'Page '.$self->pageNumber.' of '.$self->pageTotal )->italic()->face('Arial')->size(8)->positionBottomLeft()->height(15);
});
$pdf->font->face('Times')->size(12);
for($i=1;$i<=40;$i++) {
$pdf->createCell('Printing line number '.$i)->block();
}
$pdf->Output();
Note it’s much shorter. Other notes.
The biggest difference is the header isn’t a function in an extended class. That wasn’t necessary. The footer however does have to get page number and total on the fly, so a closure is used. For even more demanding customization the Footer and Header class can be extended, as can any other object in the system. Documents, sections, et al can be instructed to use your custom object(s) as necessary.
Font assignments and object properties will cascade - though not as much as in CSS (implementing such in PHP would be a nightmare). That is, if you don’t set a cell’s font, it will use the font of the object that contains it. If that object doesn’t have a font, the page, section, then document are checked.
The largest difference though, which is implementation level and only hinted at here, is that this library will only create the PDF buffer at output time. Until that time you can hold references to cells, pages and so on and modify their contents or appearance based on whatever is going on in your code. This as opposed to the current FPDF library, which requires you to create cells, pages, etc. sequentially in the order they appear in the document. The flexibility of not being required to do that is, more than anything else, the goal that’s driving me to do this work.
Thoughts?