#/usr/bin/perl -w
# GenerateBarcode.pl
# by ntheory
#
# Generates barcodes and the CRC digit for CoinStar receipts.
#
# June 26th, 2003 - Started development.
# June 30th, 2003 - After a few day haitus I resumed work and finished the code.
#
# Notes: Put the barcodes in a text file and redirect the file into STDIN.
# You should only have the 12-digits of the barcode on each line and
# nothing else. Alternatively you can enter them by hand.
use Image::Magick;
# Set up some constants that we'll need in the binary string generation phase.
$StartEndSentinel = "101"; # This goes at the beginning and end.
$CenterGuardPattern = "01010"; #This goes in the middle.
$StartEndSentinelLength = length($StartEndSentinel);
$CenterGuardPatternLength = length($CenterGuardPattern);
# These are just defined for readability purposes.
$OddParity = 0;
$EvenParity = 1;
$ManufacturerCodeLength = 5;
$WholeLeftSideLength = $ManufacturerCodeLength + 1;
$RequiredInputLength = 12;
$RightSideLength = 5;
$WholeRightSideLength = $RightSideLength + 1;
$BarcodeHeight = 50;
$SingleDigitLength = 7;
# Get the parity map, the left hand coding table, and the right hand coding table.
$ParityMap = GetParityMap();
$LeftHandCoding = GetLeftHandCodingTable();
$RightHandCoding = GetRightHandCodingTable();
$BarcodeCounter = 0;
# We'll do this in a loop so you can generate many barcodes in a row.
while (<>) {
# Get the unprocessed data and remove the trail ing newl i ne .
$Unprocessed = $_;
chomp($Unprocessed);
# Check to make sure that it makes sense.
if ( LooksValid($Unprocessed) ) {
# Now convert it to a string of binary digits using some EAN-13 magic.
# (http://www.barcodeisland.com/ean13.phtml)
$Processed = $StartEndSentinel;
# Get the first digit (determines the parity of the manufacturer code).
$FirstDigit = int( substr( $Unprocessed, 0, 1 ) );
# Encode the second digit (always odd parity).
$Offset = 1;
$CurrentDigit = int( substr( $Unprocessed, $Offset, 1 ) );
$Processed .= $LeftHandCoding[$CurrentDigit][$OddParity];
# Next encode the manufacturer code.
for ( $Loop = 0 ; $Loop < $ManufacturerCodeLength ; $Loop++ ) {
# Move to the next character .
$Offset++;
# Get the parity.
$CurrentParity =
substr( $ParityMap[$FirstDigit], $Loop, 1 ) eq "0"
? $OddParity
: $EvenParity;
$CurrentDigit = int( substr( $Unprocessed, $Offset, 1 ) );
$Processed .= $LeftHandCoding[$CurrentDigit][$CurrentParity];
}
# Slap the center guard pattern in there.
$Processed .= $CenterGuardPattern;
# Encode the right hand side.
for ( $Loop = 0 ; $Loop < $RightSideLength ; $Loop++ ) {
# Move to the next character.
$Offset++;
$CurrentDigit = int( substr( $Unprocessed, $Offset, 1 ) );
$Processed .= $RightHandCoding[$CurrentDigit];
}
# Finally encode the check digit and slap the end sentinel in there. We also tack
# the check digit onto the unprocessed string so we can draw it onto the image below in a loop.
$CheckDigit = CalculateCheckDigit($Unprocessed);
$Unprocessed .= $CheckDigit;
$Processed .= $RightHandCoding[$CheckDigit];
$Processed .= $StartEndSentinel;
# Now generate the image
$XOffset = 100;
$YOffset = 100;
$BarcodeLength = length($Processed);
$BarcodeImage = new Image::Magick;
$BarcodeGeometry = ( $BarcodeLength + $XOffset * 2 ) . "x"
. ( $BarcodeHeight + $YOffset * 2 );
$BarcodeImage->set( size => $BarcodeGeometry );
$BarcodeImage->Read("gradient:white-white");
# Don't ask me why I always do this.
#Draw the barcode.
$YMin = $YOffset;
$YMax = $YOffset + $BarcodeHeight;
for ( $Loop = 0 ; $Loop < $BarcodeLength ; $Loop++ ) {
$X = $Loop + $XOffset;
$PointsString = "$X,$YMin,$X,$YMax";
$StrokeColor =
substr( $Processed, $Loop, 1 ) eq "0" ? "White" : "Black";
$BarcodeImage->Draw(
primitive => "Line",
points => "$PointsString",
stoke => "$StrokeColor"
);
}
# Add in the digits below the barcode.
# First clear out some space for the left digits.
$BoxUpperLeftX = $StartEndSentinelLength + $XOffset;
$BoxUpperLeftY = $BarcodeHeight - 10 + $YOffset;
$BoxLowerRightX =
$BoxUpperLeftX + $SingleDigitLength * $WholeLeftSideLength;
$BoxLowerRightY = $BarcodeHeight + $YOffset;
$PointsString =
"$BoxUpperLeftX,$BoxUpperLeftY,$BoxLowerRightX,$BoxLowerRightY";
$BarcodeImage->Draw(
primitive => "Rectangle",
points => "$PointsString",
fill => "White",
stroke => "White"
);
# Draw the left side digits
$Y = $YOffset + $BarcodeHeight;
for ( $Loop = 1 ; $Loop < ( $WholeLeftSideLength + 1 ) ; $Loop++ ) {
$X = $BoxUpperLeftX + 1 + ( ( $Loop - 1 ) * $SingleDigitLength );
$Character = substr( $Unprocessed, $Loop, 1 );
$BarcodeImage->Annotate(
text => $Character,
pointsize->11,
antialias => "true",
x => $X,
y => $Y,
fill => "Black"
);
}
# Then clear out some space for the right digits.
$BoxUpperLeftX = $BoxLowerRightX + $CenterGuardPatternLength;
$BoxLowerRightX =
$BoxUpperLeftX + ( $WholeRightSideLength * $SingleDigitLength ) - 1;
$PointsString =
"$BoxUpperLeftX,$BoxUpperLeftY,$BoxLowerRightX,$BoxLowerRightY";
$BarcodeImage->Draw(
primitive => "Rectangle",
points => "$PointsString",
fill => "White",
stroke => "White"
);
# Draw the right side digits.
for ( $Loop = 1 ; $Loop < ( $WholeRightSideLength + 1 ) ; $Loop++ ) {
$X = $BoxUpperLeftX + ( ( $Loop - 1 ) * $SingleDigitlength );
$Character =
substr( $Unprocessed, $Loop + $WholeRightSideLength, 1 );
$BarcodeImage->Annotate(
text => $Character,
pointsize => 11,
antialias => "true",
x => $X,
y => $Y,
fill => "Black"
);
}
# Draw the first very first digit.
$X = $XOffset - 12;
$Character = substr( $Unprocessed, 0, 1 );
$BarcodeImage->Annotate(
text => $Character,
pointsize => 11,
antialias => "true",
x => $X,
y => $Y,
fill => "Black"
);
$BarcodeCounterString = sprintf( "%03d", $BarcodeCounter );
$BarcodeImageName = "CoinStar-$BarcodeCounterString.png";
$BarcodeImage->Write($BarcodeImageName);
$BarcodeCounter++;
# Let the user know that something happened.
$TransactionID = substr( $Unprocessed, 3, 4 );
$Amount = sprintf( "\$%3d.%02d",
int( substr( $Unprocessed, 7, 3 ) ),
int( substr( $Unprocessed, 10, 2 ) ) );
print
"Barcode generation for transaction $Transaction ID ($Amount) was successful.\n";
print
"The image was stored as $BarcodeImageName. The CoinStar check digit was $CheckDigit.\n";
}
}
sub LooksValid {
$Data = $_[0];
$ReturnValue = 0;
if ( length($Data) != $RequiredInputlength ) {
print
"Wrong number of characters. $RequiredlnputLength characters are needed to generate a barcode.\n";
}
elsif ( $Data =~ m/[^0-9]/ ) {
print
"There was a non-numeric character in your data. Only numeric data is accepted.\n";
}
else {
$ReturnValue = 1;
}
return $ReturnValue;
}
sub CalculateCheckDigit {
$Data = $_[0];
# CoinStar really exhausted their technician's with this one. Below the code
# may look very similar to a typical EAN-13 checksum calculation but it has
# a surprise ending.
$Sum = 0;
# Do the weighted sum.
for ( $Loop = 0 ; $Loop < $RequiredlnputLength ; $Loop++ ) {
$CurrentDigit = int( substr( $Data, $Loop, 1 ) );
# Even digits are added to the sum normally while odd digits are multiplied
# by three before they're added.
if ( $Loop % 2 == 0 ) {
$Sum += $CurrentDigit;
}
else {
$Sum += ( $CurrentDigit * 3 );
}
}
# Mod the sum by 10.
$Sum = $Sum % 10;
# Normally here we'd subtract the sum from 10 but CoinStar had to be
# different. CoinStar has a Spinal Tap fetish ("We've got 11").
$CheckDigit = 11 - $Sum;
# Make sure the check digit is less than 10.
$CheckDigit = $CheckDigit % 10;
return $CheckDigit;
}
sub GetParityMap {
# This table tells us how to code the manufacturer's code.
my $ParityMap;
$ParityMap[0] = "00000";
$ParityMap[1] = "0E0EE";
$ParityMap[2] = "0EE0E";
$ParityMap[3] = "0EEE0";
$ParityMap[4] = "E00EE";
$ParityMap[5] = "EE00E";
$ParityMap[6] = "EEE00";
$ParityMap[7] = "E0E0E";
$ParityMap[8] = "E0EE0";
$ParityMap[9] = "EE0E0";
return $ParityMap;
}
sub GetLeftHandCodingTable {
# This table gives us the binary representation of the left hand digits.
my $LeftHandCodingTable;
$LeftHandCoding[0][$OddParity] = "0001101";
$LeftHandCoding[1][$OddParity] = "0011001";
$LeftHandCoding[2][$OddParity] = "0010011";
$LeftHandCoding[3][$OddParity] = "0111101";
$LeftHandCoding[4][$OddParity] = "0100011";
$LeftHandCoding[5][$OddParity] = "0110001";
$LeftHandCoding[6][$OddParity] = "0101111";
$LeftHandCoding[7][$OddParity] = "0111011";
$LeftHandCoding[8][$OddParity] = "0110111";
$LeftHandCoding[9][$OddParity] = "0001011";
$LeftHandCoding[0][$EvenParity] = "0100111";
$LeftHandCoding[1][$EvenParity] = "0110011";
$LeftHandCoding[2][$EvenParity] = "0011011";
$LeftHandCoding[3][$EvenParity] = "0100001";
$LeftHandCoding[4][$EvenParity] = "0011101";
$LeftHandCoding[5][$EvenParity] = "0111001";
$LeftHandCoding[6][$EvenParity] = "0000101";
$LeftHandCoding[7][$EvenParity] = "0010001";
$LeftHandCoding[8][$EvenParity] = "0001001";
$LeftHandCoding[9][$EvenParity] = "0010111";
return $LeftHandCoding;
}
sub GetRightHandCodingTable {
# This table gives us the binary representation of the right hand digits.
my $RightHandCoding;
$RightHandCoding[0] = "1110010";
$RightHandCoding[1] = "1100110";
$RightHandCoding[2] = "1101100";
$RightHandCoding[3] = "1000010";
$RightHandCoding[4] = "1011100";
$RightHandCoding[5] = "1001110";
$RightHandCoding[6] = "1010000";
$RightHandCoding[7] = "1000100";
$RightHandCoding[8] = "1001000";
$RightHandCoding[9] = "1110100";
return $RightHandCoding;
}
syntax highlighted by Code2HTML, v. 0.9.1