Creating press-ready PDF documents on the fly including individual barcodes

Often people would like to create a PDF in offset quality out of a big bunch of addresses and create a barcode for each address. Sure, you could do this using the serial-letter-function in Word or any PHP library – the problem is that you have to stick to special standards, e. g. using CMYK colorspace and the like.
There may be other good solutions for that. Anyway, now let’s talk about doing that by using PDFLib – in the lite (and therefor free) version.
For this example of an exhibition ticket creation I am using PDFlib lite 6.0.3 and PHP 5.1.4, JPGraph 2.1p is responsible for the creation of the barcodes.

createBarcode.php

We will start with the most simple task and write the code for the creation of the barcode.
You need an installed version of JPGraph 2.x from aditus.nu.

Then you simply include the base class and the classes required for the barcode functionality:

 require_once('/path/to/jpgraph-2.1p/src/jpgraph.php');
 require_once('/path/to/jpgraph-2.1p/src/jpgraph_barcode.php');
 require_once('/path/to/jpgraph-2.1p/src/jpgraph_canvas.php');

Then set the encoding to Code 39…

 $symbology = BarcodeFactory::Create(ENCODING_CODE39);

… and the output mode to bitmap:

 $barcode = BackendFactory ::Create('IMAGE', $symbology);

We don’t want the code written below, so we simply hide it:

 $barcode->HideText();

And finally, output the data:

 $barcode->Stroke($_GET['code']);

As you can see here we use the data submitted via $_GET for encoding.

That’s it – now off for something completely different ;-)

createPDF.php

First we need to get the bunch of addresses, which can be retrieved from wherever you want. To avoide getting this article to complex I simply set up an array – but of course you can do that with database queries, files or whatever.

 $visitors[] =   array('name' => 'Jason W. Easter', 'code' => '1234567890');
 $visitors[] =   array('name' => 'John Doe', 'code' => '0987654321');
 $visitors[] =   array('name' => 'Jane Doe', 'code' => '1234509876');

Measures in PDFlib are in DTP points which is 1/72 of an inch. As I live in a country where metric measures are standard I prefer placing my data in millimeters so I have to do the conversion:

 define('PT', 0.3528);

From now on instead of placing an element e.g. 8.5 points from the left I place it (3 / PT) millimeters from the left.

Then I define the absolute path to where my scripts reside.

 define('PATH', '/path/to/htdocs/blog_thinkphp/tickets/');

We have two possibilities to create this pdf file: In the browser window or on the file system. Not to create unnecessary obstacles we define a constant which – by setting it to true or false – manages the output.

 define('PREVIEW_IN_BROWSER', false);

With this we are ready to lift off!

We simply instantiate the pdflib library …

 $pdf    =&  new PDFlib();

… and open a new document:

 $pdf->begin_document($filename, 'compatibility=1.6');

Here, depending which kind of output we want $filename is empty (“) or the absolute path and a filename (e.g. /path/to/my.pdf) and the pdf version is set to 1.6 which is according to Acrobat 7.

Then we need to set some meta data…

 $pdf->set_info('Creator', 'PDFlib');
 $pdf->set_info('Author', 'Jason W. Easter');
 $pdf->set_info('Title', 'tickets');

… and to define a search path, which is the place where pdflib looks for files etc:

 $pdf->set_parameter('SearchPath', PATH);

After loading a built-in font …

 $font['Helvetica']  =   $pdf->load_font('Helvetica', 'host', '');

…we can start filling content into the pdf document.

BEFORE iterating the address array we load the logo file:

 $logo   =   $pdf->load_image('tiff', 'think-php.tif', '');

Here the first parameter indicates what image type we use. Auto is the most flexible value, but also the slowest, because php/pdflib needs the time to detect the type.

Then we iterate the address array …

foreach ($visitors AS $visitor_id => $visitor) {

…and create one page for each data set:

 $pdf->begin_page_ext(85 / PT, 55 / PT, '');

Here the width of the page is set to 85 mm and the height to 55 mm.

Now we can start filling the page with content. First of all we place the logo file we loaded before:

 $pdf->fit_image($logo, 5 / PT, 37 / PT, 'scale 1.0');

We also need some text. The headline…

 $pdf->setfont($font['Helvetica'], 14.0); // set font-face and -size
 $pdf->set_text_pos(35 / PT, 41.5 / PT); // set position
 $pdf->show('1. thinkPHP day'); // display

…and the name and the code:

 $pdf->setfont($font['Helvetica'], 11.0);
 $pdf->set_text_pos(25 / PT, 32 / PT);
 $pdf->show($visitor['name']);
 $pdf->setfont($font['Helvetica'], 9.0);
 $pdf->continue_text($visitor['code']);

And finally we get the barcode and place it.
PDFlib is unable to load files from an url, so we first have to create a “pdflib virtual file” (=pvf)…

 $imgdata = implode('', file('http://path/to/createBarcode.php?code=' . $visitor['code']));

 $tmpimgfile = '/pvf/'.$visitor_id.'.tif';
 $pvf_handle = $pdf->create_pvf($tmpimgfile, $imgdata, '');

…and then load the barcode image into it:

 $image = $pdf->load_image('auto', $tmpimgfile, '');
 $pdf->fit_image($image, 0 / PT, 2.5 / PT, 'scale 1.0 boxsize {'.(85 / PT).' ' .(20 / PT).'} position 50 fitmethod meet');
 $pdf->close_image($image);

After that we can delete the pvf…

$pdf->delete_pvf($pvf_handle);

…and close the page:

 $pdf->end_page_ext('');

After the closing foreach-bracket we need to close the logo image file…

 $pdf->close_image($logo);

… close the document…

 $pdf->end_document('');

…and conditionally output the created pdf to the browser:

 if (PREVIEW_IN_BROWSER === true)  {
     $buf = $pdf->get_buffer();
     $len = strlen($buf);
     header ('Content-type: application/pdf');
     header ('Content-Length: ' . $len);
     header ('Content-Disposition: inline; filename=blog_thinkphp.pdf');
     print $buf;
 }

That’s it!

You now may wonder why the logo file is opened and closed outside the foreach-block while we open, place and close the barcode inside.
Well, the reason for that is simple: Speed and space.
The logo doesn’t change, it always remains the same. If I only load it once I save the time for opening it within each page. And, nevertheless, I make use of a pdf technology called XObject. This has the effect, that only a reference to an image is placed at each page instead of the image data itself; that’s how I save lots of space.
E.g. if I have 10 addresses and create a pdf out of them, with XObject it takes me just 61 KB of space, without XObject it takes me 486 KB.
Now imagine you do that with many more addresses!

Example application: pdf_Creation.zip

Für neue Blogupdates anmelden:


3 Gedanken zu “Creating press-ready PDF documents on the fly including individual barcodes

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.