POSTNET barcodes

Informations

Author: Shailesh Humbad
License: FPDF

Description

This is an extension for printing U.S. Postal Service POSTNET barcodes in PDF. It supports both 5 and 9 digit zip codes and includes a utility function for parsing zip codes from an address line. The barcode is drawn directly in PDF without using an image or special font.

POSTNETBarCode(float x, float y, string zipcode)

x: abscissa of barcode
y: ordinate of barcode
zipcode: zip code to draw

$zipcode must be a string containing a zip code of the form DDDDD or DDDDD-DDDD. This function will trigger a PHP error if the zip code is invalid, so if the string's validity is in question, use ParseZipCode first. The parameters $x and $y specify the lower left corner of the barcode in user units. This function changes the line width in the FPDF context.

string ParseZipCode(string stringToParse)

The parsed zip code returned will be the first string matching DDDDD or DDDDD-DDDD, in that order, where D is a digit from 0 through 9. The search begins at the end of $stringToParse.
If a valid zip code cannot be found, it returns an empty string. Otherwise, it returns the parsed zip code (including the hyphen if it's a 9-digit zip).

An online example is available here.

POSTNET BARCODES

The specifications for POSTNET Barcodes can be found in U.S. Postal Service Publication 25, Designing Letter and Reply Mail. See Chapter 2 for barcode placement specifications, and Chapter 4 for barcode reading and writing specifications.

You can print barcodes on papers that will be inserted into envelopes with windows, or directly on envelopes. For envelopes with windows, in general, the barcode should be printed a minimum of 1/25" above the first line of the address, and below the top edge of the envelope window. The left and right clearance for the address should be a minimum of 1/8", and the bottom clearance should be 1/25" minimum. The address must satisfy the minimum clearances regardless of where the mailpiece shifts to within the envelope. Note that 1/4" of clearance is preferred.

Refer to the Guidelines for complete details.

Source

<?php
require('fpdf.php');

class PDF_POSTNET extends FPDF
{
    // PUBLIC PROCEDURES

    // draws a bar code for the given zip code using pdf lines
    // triggers error if zip code is invalid
    // x,y specifies the lower left corner of the bar code
    function POSTNETBarCode($x, $y, $zipcode)
    {
        // Save nominal bar dimensions in user units
        // Full Bar Nominal Height = 0.125"
        $FullBarHeight = 9 / $this->k;
        // Half Bar Nominal Height = 0.050"
        $HalfBarHeight = 3.6 / $this->k;
        // Full and Half Bar Nominal Width = 0.020"
        $BarWidth = 1.44 / $this->k;
        // Bar Spacing = 0.050"
        $BarSpacing = 3.6 / $this->k;

        $FiveBarSpacing = $BarSpacing * 5;

        // 1 represents full-height bars and 0 represents half-height bars
        $BarDefinitionsArray = Array(
            1 => Array(0,0,0,1,1),
            2 => Array(0,0,1,0,1),
            3 => Array(0,0,1,1,0),
            4 => Array(0,1,0,0,1),
            5 => Array(0,1,0,1,0),
            6 => Array(0,1,1,0,0),
            7 => Array(1,0,0,0,1),
            8 => Array(1,0,0,1,0),
            9 => Array(1,0,1,0,0),
            0 => Array(1,1,0,0,0));
            
        // validate the zip code
        $this->_ValidateZipCode($zipcode);

        // set the line width
        $this->SetLineWidth($BarWidth);

        // draw start frame bar
        $this->Line($x, $y, $x, $y - $FullBarHeight);
        $x += $BarSpacing;

        // draw digit bars
        for($i = 0; $i < 5; $i++)
        {
            $this->_DrawDigitBars($x, $y, $BarSpacing, $HalfBarHeight, 
                $FullBarHeight, $BarDefinitionsArray, $zipcode{$i});
            $x += $FiveBarSpacing;
        }
        // draw more digit bars if 10 digit zip code
        if(strlen($zipcode) == 10)
        {
            for($i = 6; $i < 10; $i++)
            {
                $this->_DrawDigitBars($x, $y, $BarSpacing, $HalfBarHeight, 
                    $FullBarHeight, $BarDefinitionsArray, $zipcode{$i});
                $x += $FiveBarSpacing;
            }
        }
        
        // draw check sum digit
        $this->_DrawDigitBars($x, $y, $BarSpacing, $HalfBarHeight, 
            $FullBarHeight, $BarDefinitionsArray, 
            $this->_CalculateCheckSumDigit($zipcode));
        $x += $FiveBarSpacing;

        // draw end frame bar
        $this->Line($x, $y, $x, $y - $FullBarHeight);

    }

    // Reads from end of string and returns first matching valid
    // zip code of form DDDDD or DDDDD-DDDD, in that order.
    // Returns empty string if no zip code found.
    function ParseZipCode($stringToParse)
    {
        // check if string is an array or object
        if(is_array($stringToParse) || is_object($stringToParse))
        {
            return "";
        }

        // convert parameter to a string
        $stringToParse = strval($stringToParse);

        $lengthOfString = strlen($stringToParse);
        if ( $lengthOfString < 5 ) {
            return "";
        }
        
        // parse the zip code backward
        $zipcodeLength = 0;
        $zipcode = "";
        for ($i = $lengthOfString-1; $i >= 0; $i--)
        {
            // conditions to continue the zip code
            switch($zipcodeLength)
            {
            case 0:
            case 1:
            case 2:
            case 3:
                if ( is_numeric($stringToParse{$i}) ) {
                    $zipcodeLength += 1;
                    $zipcode .= $stringToParse{$i};
                } else {
                    $zipcodeLength = 0;
                    $zipcode = "";
                }
                break;
            case 4:
                if ( $stringToParse{$i} == "-" ) {
                    $zipcodeLength += 1;
                    $zipcode .= $stringToParse{$i};
                } elseif ( is_numeric($stringToParse{$i}) ) {
                    $zipcodeLength += 1;
                    $zipcode .= $stringToParse{$i};
                    break 2;
                } else {
                    $zipcodeLength = 0;
                    $zipcode = "";
                }
                break;
            case 5:
            case 6:
            case 7:
            case 8:
                if ( is_numeric($stringToParse{$i}) ) {
                    $zipcodeLength = $zipcodeLength + 1;
                    $zipcode = $zipcode . $stringToParse{$i};
                } else {
                    $zipcodeLength = 0;
                    $zipcode = "";
                }
                break;
            case 9:
                if ( is_numeric($stringToParse{$i}) ) {
                    $zipcodeLength = $zipcodeLength + 1;
                    $zipcode = $zipcode . $stringToParse{$i};
                    break;
                } else {
                    $zipcodeLength = 0;
                    $zipcode = "";
                }
                break;
            }
        }

        // return the parsed zip code if found
        if ( $zipcodeLength == 5 || $zipcodeLength == 10 ) {
            // reverse the zip code
            return strrev($zipcode);
        } else {
            return "";
        }

    }

    // PRIVATE PROCEDURES

    // triggers user error if the zip code is invalid
    // valid zip codes are of the form DDDDD or DDDDD-DDDD
    // where D is a digit from 0 to 9, returns the validated zip code
    function _ValidateZipCode($zipcode)
    {
        $functionname = "ValidateZipCode Error: ";

        // check if zipcode is an array or object
        if(is_array($zipcode) || is_object($zipcode))
        {
            trigger_error($functionname.
                "Zip code may not be an array or object.", E_USER_ERROR);
        }

        // convert zip code to a string
        $zipcode = strval($zipcode);

        // check if length is 5
        if ( strlen($zipcode) != 5 && strlen($zipcode) != 10 ) {
            trigger_error($functionname.
                "Zip code must be 5 digits or 10 digits including hyphen. len:".
                strlen($zipcode)." zipcode: ".$zipcode, E_USER_ERROR);
        }

        if ( strlen($zipcode) == 5 ) {
            // check that all characters are numeric
            for ( $i = 0; $i < 5; $i++ ) {
                if ( is_numeric( $zipcode{$i} ) == false ) {
                    trigger_error($functionname.
                        "5 digit zip code contains non-numeric character.",
                        E_USER_ERROR);
                }
            }
        } else {
            // check for hyphen
            if ( $zipcode{5} != "-" ) {
                trigger_error($functionname.
                    "10 digit zip code does not contain hyphen in right place.",
                    E_USER_ERROR);
            }
            // check that all characters are numeric
            for ( $i = 0; $i < 10; $i++ ) {
                if ( is_numeric($zipcode{$i}) == false && $i != 5 ) {
                    trigger_error($functionname.
                        "10 digit zip code contains non-numeric character.",
                        E_USER_ERROR);
                }
            }
        }

        // return the string
        return $zipcode;
    }

    // takes a validated zip code and 
    // calculates the checksum for POSTNET
    function _CalculateCheckSumDigit($zipcode)
    {
        // calculate sum of digits
        if( strlen($zipcode) == 10 ) {
            $sumOfDigits = $zipcode{0} + $zipcode{1} + 
                $zipcode{2} + $zipcode{3} + $zipcode{4} + 
                $zipcode{6} + $zipcode{7} + $zipcode{8} + 
                $zipcode{9};
        } else {
            $sumOfDigits = $zipcode{0} + $zipcode{1} + 
                $zipcode{2} + $zipcode{3} + $zipcode{4};
        }

        // return checksum digit
        if( ($sumOfDigits % 10) == 0 )
            return 0;
        else
            return 10 - ($sumOfDigits % 10);
    }

    // Takes a digit and draws the corresponding POSTNET bars.
    function _DrawDigitBars($x, $y, $BarSpacing, $HalfBarHeight, $FullBarHeight,
        $BarDefinitionsArray, $digit)
    {
        // check for invalid digit
        if($digit < 0 && $digit > 9)
            trigger_error("DrawDigitBars: invalid digit.", E_USER_ERROR);
        
        // draw the five bars representing a digit
        for($i = 0; $i < 5; $i++)
        {
            if($BarDefinitionsArray[$digit][$i] == 1)
                $this->Line($x, $y, $x, $y - $FullBarHeight);
            else
                $this->Line($x, $y, $x, $y - $HalfBarHeight);
            $x += $BarSpacing;
        }
    }

}
?>

Example

<?php
require('postnet.php');

$pdf = new PDF_POSTNET("P","pt");
$pdf->AddPage();
$pdf->SetFont("Arial","",10);

// ParseZipCode examples
//$stringToParse = "Ann Arbor, MI 48109-110asdf"; // returns "48109"
//$stringToParse = "Ann Arbor, MI 48109-110"; // returns "48109"
//$stringToParse = "Ann Arbor, MI 481091109"; // returns "91109"
//$stringToParse = "Ann Arbor, MI 48109-1109asdf"; // returns "48109-1109"
//$stringToParse = "Cambridge, MA 0192"; // returns empty string
//$stringToParse = "Cambridge, MA 02139"; // perfect, returns "01239"
$stringToParse = "Ann Arbor, MI 48109-1109"; // perfect, returns "48109-1109"

$zipcode = $pdf->ParseZipCode($stringToParse);
$pdf->POSTNETBarCode(40,40,$zipcode);
$pdf->Text(40,90,$zipcode);

$pdf->Output();
?>
View the result here.

Download

ZIP | TGZ