[barcode] Postnet suppport

Dominik Seichter domseichter@web.de
Fri, 21 Feb 2003 16:46:31 +0100


--Boundary-00=_XnkV+OehiT2kh4Q
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Hello,

First off all, Stonki (from the KBarcode team) reportet the following bug some 
time ago:
> >We also discover a heavy bug in the Library of barcode, which causes
> >frequent crashes. I hope, that I can create a test for showing it.
> Describing it may be sufficient I think. Any feedback is welcome.
> Obviously if serious bugs are fixed the urge to make a new official
> release is greately increased.
I thought it was a bug, but it was another mistake in my code, so sorry for 
bothering you - it was my mistake. GNU Barcode is perfectly OK. I should have 
finished the testcase first before letting someone report the bug.

The main reason for my mail is that I think the support for US Postal or 
Postnet barcodes would be a good addition to GNU barcode.
I even got some code for the generation of Postnet barcodes from a guy called 
Tony O'Bryan. I ported the code from c++ back to C today and integrated it in 
GNU barcode. I attached the source file to this mail. I did not add patches 
for the Makefile and for my changes to barcode.h (add BARCODE_POSTNET to 
enum), main.cpp (add a postnet commandline encoding option) and library.c 
(add extern definitions for postnet.c and add it to encodings), because this 
changes are rather trivial. If you want I can provide patches for those, too.

If you know Postnet barcodes allready you will know that the code can't work 
yet. According to my source of information 
http://pe.usps.gov/text/Pub25/Pub25ch4.htm Postnet barcodes consist of a 
series of tall and low bars, which is AFAIK currently not possible using GNU 
Barcode. Is it currently possible? If yes how can do it? If no is their any 
chance that it will be added?

The current postnet code is not yet finished and the barcodes that get created 
are simply stupid, because tall and low bars cannot been drawn yet. I needs 
several clean up's, simplifications and DPBC support is not yet added. I will 
do this as soon as GNU Barcode can handle tall and low bars.

Feel free to add this code to the GNU Barcode distribution, if it is of any 
use for you. 

And last but not least, thanks for this great application GNU Barcode which 
made KBarcode possible.

CU Dom (programmer of KBarcode)
-- 
************************************************************
Dominik Seichter - domseichter@web.de
Krename - http://www.krename.net
KBarcode - http://www.kbarcode.net
************************************************************

--Boundary-00=_XnkV+OehiT2kh4Q
Content-Type: text/x-csrc;
  charset="us-ascii";
  name="postnet.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="postnet.c"

/*
 * Copyright 2001 by Tony O'Bryan.  All Rights Reserved.
 *
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License Version 2 as     *
 *   published by the Free Software Foundation.                            *
 *                                                                         *
 ***************************************************************************
 *
 * When looking for information on postal bar codes, I searched Google and
 * found this URL (among many): http://www.msoe.edu/~taylor/cs150/lab5.htm.
 * The code in this class is based upon the following information on that
 * web page:
 *
 * "Postal bar codes are used by the United States Postal Service to
 * automate mail sorting. The bar codes are more easily scanned by a computer
 * that the numbers we like to use. A bar code consists of a sequence of tall
 * and short bars. There is a tall bar at the beginning and end of the bar
 * code. Each digit is represented by 5 bars and an additional 5 bars are used
 * to check the validity of the bar code. A total of 32 bars are used to
 * represent a five digit zipcode. For example, the bar code for 53222 is:
 *
 * |:|:|:::||:::|:|::|:|::|:|:||::| where | represents a tall bar and :
 * represents a short bar.
 *
 * Each digit in the code is converted as follows:
 *
 * 1 -- :::||
 * 2 -- ::|:|
 * 3 -- ::||:
 * 4 -- :|::|
 * 5 -- :|:|:
 * 6 -- :||::
 * 7 -- |:::|
 * 8 -- |::|:
 * 9 -- |:|::
 * 0 -- ||:::
 *
 * To find the value of the check digit, you need to add all of the digits
 * together and choose the check digit to make the sum a multiple of 10.
 * (In the above example I chose the check digit to be 6 because
 * 5+3+2+2+2+6=20.)" -- Copyright 2000 by Dr. Christopher C. Taylor
 * (taylor@msoe.edu).
 *
 * Additional information was found on the U.S. Postal Service publications
 * page at http://pe.usps.gov/text/Pub25/Pub25ch4.htm.  The Delivery Point
 * Bar Code information is based on the above page:
 *
 * "The DPBC is formed by adding 10 bars to an existing ZIP+4 barcode (see
 * Exhibit 4-1). The 10 bars represent two additional digits (normally the
 * last two digits of the street address, post office box, rural route number,
 * or highway contract route number). DMM C840 contains address coding rules
 * for the DPBC, including rules for handling address anomalies."
 */

 /* 
  * Ported to GNU Barcode by
  * Dominik Seichter <domseichter@web.de>
  *
  */  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include "barcode.h"

#define FALSE 0
#define TRUE !FALSE
/*
 * Makes sure that the given string consists solely of the given quantity of
 * numeric digits.
 *
 * Returns true if the string consists solely of numeric digits, or false
 * if any digit is non-numeric, or if there are more or less than the given
 * quantity of digits.
 */
int isNumeric(char* cString,int nNumDigits)
{
    int	bRet = 1;
    int i = 0;
    int nLength = strlen( cString );

    if (nLength == nNumDigits)
    {
	for ( i = 0;i < nLength;i++)
	{
	    if (isdigit(cString[i]) == 0)
	    {
		bRet = 0;
		break;
	    }
	}
    }
    else
    {
	bRet = 0;
    }
    return bRet;
}

char* substr( const char* src, int start, int end )
{
    int i = 0;
    char* ret = malloc( sizeof( char ) * ( end - start + 1 ) );
    
    for( i = start; i < end; i++ )
	ret[i-start] = src[i];

    ret[end - start] = '\0';
    return ret;
}

int isnum( char* s )
{
    int i = 0;
    int len = strlen( s );
    for( i = 0; i < len; i++ )
	if( !isdigit( s[i] ) )
	    return FALSE;

    return TRUE;
}

/*
 * Makes sure that the zip code contains 5 numeric digits; or 5 numeric
 * digits, a dash, and 4 numeric digits.  The stDpbc must either be a 2-
 * digit number or be blank.  If it is blank, then it is ignored.
 *
 * Returns true if the zip code is valid, or false if it violates the above
 * conditions.
 */
int Barcode_postnet_verify(unsigned char *text)
{
    char* stZip = (char*)text;
    char* stDpbc = "";

    int	nLength = strlen( stZip );
    int	bRet = TRUE;

    if (nLength == 5) {
	bRet = isnum(stZip);
    } else if (nLength == 10) {
	char* stLeft = substr( stZip, 0, 5 );
	char* stRight = substr( stZip, 6, 10 );

	if (stZip[5] == '-') { 
	    bRet = isnum(stLeft) && isnum(stRight);
	} else {
	    bRet = FALSE;
	}

	free( stLeft );
	free( stRight );
    } else {
	bRet = FALSE;
    }

    if( stDpbc ) {
	if ( strlen( stDpbc ) == 2) {
	    bRet = bRet && isnum(stDpbc);
	} else if ( strlen(stDpbc) != 0) {
	    bRet = TRUE;
	}
    }

    return !bRet;
}

/*
 * Generates the actual bar code sequence.  The string should have already
 * been validated, as this method assumes the input is good.
 */
char* generateBarCode(char* cZip, char* stDpbc)
{
    char* stRet = malloc( sizeof(char) * 255 );
    char* stCodes[10] = {"||:::",":::||","::|:|","::||:",":|::|",":|:|:",":||::","|:::|","|::|:","|:|::"};
    char cBuffer[2] = "\0";
    int	nLength = strlen( cZip );
    int	nIndex;
    int	nSum = 0;
    int i = 0;

    stRet[0] = '|';
    stRet[1] = '\0';

    if (nLength > 0) {
	/*
	 * Do the first 5 digits.
	 */
	for ( i = 0;i < 5;i++) {
	    cBuffer[0] = cZip[i];
	    nIndex = atoi(cBuffer);
	    nSum += nIndex;
	    strcat( stRet, stCodes[nIndex] );
	}

	if (nLength == 10) {
	    /*
	     * This is in the zip+4 format (#####-####).  Skip the dash and process the last 4 digits.
	     */
	    char* stZip = substr( stZip, 6, 10 );
	    for( i = 0;i < 4;i++) {
		cBuffer[0] = cZip[i];
		nIndex = atoi(cBuffer);
		nSum += nIndex;
		strcat( stRet, stCodes[nIndex] );
	    }
	}

	nLength = strlen( stDpbc );

	if (nLength == 2) {
	    /**
	     * The DPBC (Delivery Point Bar Code) is 2 digits, so append it to the output barcode.
	     */
	    free( cZip );
	    cZip = (char *)stDpbc;
	    for ( i = 0;i < 2;i++) {
		cBuffer[0] = cZip[i];
		nIndex = atoi(cBuffer);
		nSum += nIndex;
		strcat( stRet, stCodes[nIndex] );
	    }
	}

	/*
	 * Now do the check digit.  Find the number that when added to the sum of the other characters brings us to the next highest
	 * multiple of ten.
	 */
	nIndex = nSum % 10;
	if (nIndex != 0) {
	    /*
	     * If the index is not zero, then we're not at an exact multiple of ten.  In that case we need
	     * to add (10 - index) to calculate the check digit.
	     *
	     * If the sum of the digits is already an exact multiple of ten, then the check digit must be
	     * zero (which is what nIndex will already be after the modulus was calucated).
	     */
	    nIndex = 10 - nIndex;
	}

	strcat( stRet, stCodes[nIndex] );
	strcat( stRet, "|" );
    }
    return stRet;
}

int Barcode_postnet_encode(struct Barcode_Item *bc)
{
    char* partial;
    char* result;
    int i;
    /* better safe than sorry */
    if (bc->partial)	free(bc->partial);  bc->partial =  NULL;
    if (bc->textinfo)	free(bc->textinfo); bc->textinfo = NULL;

    partial = generateBarCode( bc->ascii, "" );
    result = malloc( sizeof( char ) * strlen( partial ) * 2 + 1);
    result[0] = '0';
    result[1] = '\0';

    for( i = 0; i < strlen( partial ); i++ ) {
	if( partial[i] == '|' ) {
	    /* insert high bar */
	    strcat( result, "a4" );
	} else {
	    /* insert low bar */
	    strcat( result, "a1" );
	}
	/* insert space */
	strcat( result, "a1" );
    }

    bc->partial =result;
    bc->textinfo = strdup("");

    free( partial );
    return 0;
}


--Boundary-00=_XnkV+OehiT2kh4Q--