Stick ’em up! Easing the holds process

Starting today, patrons who have items on hold at select branches will notice something new about the material they receive: a sticky note.

The whole point of these new stickers is to ease the work created by an ever-increasing volume of holds being placed on AADL material. Our processors have been bearing the brunt of increased loads, partly due to general growth, but also because it is so easy to place a hold online now that the number of holds we get has shot up. Now, as far as the processors are concerned, when they scan material that has been routed to the correct pickup location, a custom label, replete with patron information and barcode will spit right out of a networked label printer. This removes a step or two from the process and has led to some very happy circulation staff. Though I’m quite pleased with the way the labels turned out, what pleases me the most (besides the happy processors) is what happens behind the scenes. What’s fun about this are the relatively simple back-end components that make this work.

The III circulation client can be configured to send a print job to an email address instead of a standard receipt or line printer. In this case, I’ve set up an instance of Exim running on a Linux box with an address alias that pipes the email through a very simple PHP script. The PHP script parses the email, creates a PNG on the fly, then sends it to the label printer via cupsys lp. I’ve been having a lot of fun with PHP’s GD module lately and this little project really underscores how slick it can be. In addition to GD, I also made use of PEAR’s Image::Barcode class so that the item’s barcode could be included on the tag. Because PEAR’s barcode class doesn’t allow you to return the image as an object, I had to instantiate it as a separate script. That’s fine because you can just point imagecreatefrompng to the URL. The barcode script couldn’t be simpler. Not only that, it’s so generic, you can really use it for all your barcoding needs:

<?php
// bcode.php
if (!$_GET[input]) { return; }
require_once('Image/Barcode.php');
$bcode = new Image_Barcode;
$bcode->draw($_GET[input], 'int25', 'png');
?>

‘Eh? What a great class! Here’s the guts of the label code–messy, I know, but it works. As you can see, it’s utilizing PHP’s CLI or CGI build. Yes, PHP makes a great shell scripting language too!

#!/usr/bin/php5 -q
<?php
 
putenv('GDFONTPATH=' . realpath('/usr/local/php/fpdf/ttf/'));  // Where my TTF fonts are
 
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
        $email .= fread($fd, 1024);
}
fclose($fd);
 
$item = hold_item_info($email);
 
$x = 350;
$y = 350;
$daysonshelf = 8;
$unique = time() . rand(2000000, 6000000);
$font = 'arialbd';
$logourl = "http://www.aadl.org/staticimages/aadllogo.gif";
$bcodeurl = "http://bcodeserver.aadl.org/bcode.php?input=$item[barcode]";
 
// Create the image
$im = imagecreatetruecolor($x, $y);
$im2 = imagecreatefrompng($bcodeurl);
$im3 = imagecreatefromgif($logourl);
 
$white = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0, 0, 0);
 
imagefill($im, 0, 0, $$white);
imagefilledrectangle($im, 0, 0, $x, $y, $white);
 
$spine_name = $item[plname];
if ($item[pfname]) { $spine_name .= ', ' . strtoupper($item[pfname]{1}) . '.'; }
 
$holdtil = (time() + ($daysonshelf * 86400));
$holdtilfmt = date("n/j", $holdtil);
$canceldate = date("m-d-Y", $holdtil);
 
$details = 'Title: ' . $item[title] . "\n" .
        'Author: ' . $item[author] . "\n" .
        'Callnum: ' . $item[callnum] . "\n" .
        'Barcode: ' . $item[barcode] . "\n" .
        'Held for: ' . $item[plname] . ', ' . $item[pfname] . "\n" .
        'Pickup location: ' . $item[pickuploc] . "\n" .
        'Hold until: ' . $canceldate;
 
imagettftext($im, 28, 270, 40, 20, $black, $font, $spine_name);
imagefilledrectangle($im, 72, 20, 74, 345, $black);
imagefilledrectangle($im, 15, 300, 70, 335, $white);
imagettftext($im, 12, 0, 32, 320, $black, $font, $holdtilfmt);
ImageCopyMerge($im , $im2, 85, 40, 0, 0, ImageSX($im2), ImageSY($im2), 100);
ImageCopyMerge($im , $im3, 85, 275, 0, 0, ImageSX($im3), ImageSY($im3), 100);
imagettftext($im, 12, 0, 85, 120, $black, $font, $details);
 
imagepng($im,"/tmp/pic$unique.png");
imagedestroy($im);
 
if ($item[printer]) {
        $printer = $item[printer];
        exec("/usr/bin/lp -d $printer /tmp/pic$unique.png");
}
 
unlink("/tmp/pic$unique.png");
 
// Functions Below
 
function hold_item_info($email) {
    // This function returns the $item array -- customize it for your environment.
}
?>

The script creates a PNG that looks something like this:

The PNG is sent to the Zebra S4M and printed on a label that has Post-It-like adhesive. Processors then peel off the backing and fold the sticker over the spine of the book and put it on the hold shelf.

The hardest part of this whole project was getting the printer to work with CUPS. I wound up bastardizing some existing postscript drivers through trial and error. As you can see from the photos, I still don’t have it right. I’d like the image to take up the entire sticker area. All in good time. If anyone out there wants the driver I’ll be glad to email it–maybe you can get it working better! Just ask.

[update]
This is how the labels are affixed to material:

You can overlook the fact that the label info doesn’t match the book info–it’s just for show!
[/update]

[tags]library, libraries, PHP, coding[/tags]


About this entry