|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI've decided to write this article after reading the novel 'THE ASTI SPUMANTE CODE' by Toby Clements, which is a parody of 'THE DA VINCI CODE' by Dan Brown. I'm not that obsessed with any of these books, but just thought it would be more interesting to introduce UPC-A barcoding from such a perspective; though sarcastic. Almost every item we buy or own has a barcode printed on it. We see those barcodes every day, but still we don't understand what they really mean or how to generate them. The major benefit of using barcodes is to automate and improve the speed and accuracy of data collection. That's why we see them on most sellable items. One other way to look at barcodes is to think of them as one other form of cryptography that's so easy to decipher using barcode scanners. For example, a UPC-A barcode is a simple encoding of a 12 digit number into its bit representation, where 1 is represented as black vertical line and 0 represented as white vertical line. We will be creating a Java program that just does that.
Note: Image Cropped From 'THE ASTI SPUMANTE CODE' Book Cover BackgroundIn 'The Da Vinci Code', the secret that Robert Langdon and Sophie were searching for was the Holy Grail, which the Priory of Sion has hidden for centuries. However, in 'The ASTI SPUMANTE CODE', the secret that James Crack and Emily were searching for was the greatest book that will ever be written. According to Toby Clements, 'the secret ingredients of this book - characterization, plot, setting, and so forth - what para-literalists call the Asti Spumante Code - are contained on something called the Mure-de-Paume, the legendary keystone.' While searching for the code, James Crack and Emily find a prolix, or simply what I concluded, a book. They discover under the book's red leather cover 'a rectangle of paper approximately five centimeters by three, laminated to the back of the prolix. On the piece of paper were a set of lines of differing widths and heights that could be read from right to left, or left to right, or not at all. It was a code. A barcode.' As sarcastic as this can get, it does describe what a barcode looks like. In order to be able to read the barcode, James Crack escapes to London to a bookstore owned by Donnie Dogs. Clark asks Dogs about barcodes, and Dogs explains that barcodes are named by scholars as UPC, or Universal Product Code. Manufactures buy those barcodes from UCC, or Uniform Code Council. The UPC is made up of exactly 12 digits. Each digit is represented as a stream of 7 bits, and each bit corresponds to a slice (1 for black slices and 0 for white ones). The first 6 digits for the 12-digit UPC correspond to the manufacturer code, and they are handed down by UCC. The next 5 digits correspond to the product code, and they are filled by the manufacturer. The last digit is a check digit used to help the barcode scanner make sure that it read the right code number. And as we've all seen, in a bookstore or super market, a scanner reads the barcode through red laser beams (i.e. barcode reader). In 'The ASTI SPUMANTE CODE', it is this check digit that James and Emily have to calculate in order to reach the hidden secret. Wow, what an impossible mission! The check digit is calculated by first adding the odd-spaced digits (6 characters), multiplying them by three, then adding them to the sum of the even-spaced digits (5 characters since we exclude the check digit). Then subtract from the next higher multiple of 10. For example, let's say we want to find the check digit for the following code: 31415926535. 3 * (3 + 4 + 5 + 2 + 5 + 5) + (1 + 1 + 9 + 6 + 3) = 3 * (24) + (20) = 92
==> Check code = 100 - 92 = 8.
OverviewThere are many kinds of barcodes:
The kind that Toby Clements refers to in his parody is UPC-A. In the UPC code, only the digits [0-9] are allowed. However, in other types of barcoding, you might have characters, symbols, or even images! UPC-AIn addition to the information that Dogs mentioned, there remains some useful information that we need to know about the UPC-A barcode. Structure of UPC-A Barcode
We can divide the 12 digit UPC code into two parts: the left 6 digits and the right 6 digits. Every digit has its predefined stream of bits based on whether it is a left or a right digit.
For example, the left 3 in the following code 314159265358 would be converted into 0111101, while the right 3 would be converted into 1000010. The left, middle, and right codes are used to help the scanner (or reader) identify where the code starts and ends. The number system is a single digit which identifies the type of the product.
The remaining digits should be well known by now, as explained by Dogs and as shown in the figure. The ProgramThis program converts a 12 digit code into a barcode. The whole idea is very simple: read a digit, convert it to its corresponding 7 bits based on whether it is on the left or the right, and then draw a vertical line for every 1, and skipping space for every 0. The program also includes a feature to let you calculate the check digit. In case you have entered a wrong check digit, the program doesn't display a barcode. Program View
UPC is the number entered by the user, and barcode is the alternating black and white lines (or slices) that we want to generate. How To Run The ProgramI have used BlueJ as the environment to implement this program. So if you have BlueJ, you can easily open it, view the UML diagram, and run the Using the Code
As shown in the figure above, the GUI Behavior
We have a panel ( Class DeignASBarcode
The logic of the program is found in the Initialization
CheckDigitListenerThe method below is called when the 'Generate Check Digit' button is clicked. void actionPerformed (ActionEvent e)
{
// Generate the check digit and append it at the end of the 11-digit UPC field
upcField.generateCheckDigit ();
}
BarcodeListenerThe method below is called when the 'Generate Bar Code' button is clicked. If the check digit is valid, the barcode is generated and displayed. public void actionPerformed (ActionEvent e)
{
// Generate the barcode only if the check digit is valid
if (upcField.isCheckDigitValid ())
{
// Set the UPC of the barcode label
barcodeLabel.setUPC (upcField.getUPC());
// Validate the UPC of the barcode label
barcodeLabel.validateUPC ();
// Generate the barcode
barcodeLabel.generateBarcode ();
// Select the text in the UPC field
upcField.selectAll ();
}
else
{
// Make the barcode as invalid so that nothing would be drawn on
// the barcode panel.
// Check BarcodeLabel.paintComponent
barcodeLabel.setValid (false);
}
}
BarcodeLabel
ConstantsWe first start by defining the constants: // Put in front and at end of every barcode
final String quiteZone = "000000000";
// Put after quite zone at the front - See Figure
final String leftStartCode = "101";
// Put before quite zone at the end - See Figure
final String rightEndCode = "101";
// Put in the center of every barcode - See Figure
final String centerCode = "01010";
// Represent the left UPC bits for the digits
final String leftCodes[] = {"0001101", "0011001", "0010011", "0111101",
"0100011", "0110001", "0101111", "0111011",
"0110111", "0001011"};
// Represent the right UPC bits for the digits
final String rightCodes[] = {"1110010", "1100110", "1101100", "1000010",
"1011100", "1001110", "1010000", "1000100",
"1001000", "1110100"};
// represents the number of slices in a UPC-A
final int sliceNum = 113; // 3 + 3 + 5 + (12 * 7) + (9 * 2) = 113
The quite zone, left, center, and right codes will be added to every barcode we are going to generate. We would care to know the number of slices we have in order to define the widths of the slices and of the panel that will hold them. The Instance VariablesString UPC; // UPC to be converted into barcode
String barcode; // the barcode
boolean valid; // UPC is valid or not?
Color barColor; // The color of the slice corresponding to bit 1
Color spaceColor; // The color of the slice corresponding to bit 0
Color errorColor; // In case the bit is neither 0 or 1 (something wrong)
int x; // Represents the current x coordinate of the slice
int sliceWidth; // Represents the width of the slice
int width; // Width of barcode label
int height; // height of barcode label
MethodsThe constructor sets the color and size of the barcode label. //---------------------------------------------------------------------
// Constructor...
//
// sw is the slice width and sh is the slice height.
// bc is the bar color, sc is the space color, and ec is error color.
//---------------------------------------------------------------------
public BarcodeLabel (int sw, int sh, Color bc, Color sc, Color ec)
{
// Upc is initially invalid
valid = false;
// Set x initially to zero
x = 0;
// Set the slicewidth
sliceWidth = sw;
// Set the width of barcode label
width = sliceNum * sw;
// Set the height of barcode label
height = sh;
// Set the colors
barColor = bc;
spaceColor = sc;
errorColor = ec;
// Set the background color to space color
this.setBackground (spaceColor);
// Set the preferred size of the barcode label
this.setPreferredSize (new Dimension (width, height));
}
The //---------------------------------------------------------------------
// Paints the barcode only if it is valid...
//---------------------------------------------------------------------
public void paintComponent (Graphics page)
{
super.paintComponent (page);
// Clear the barcode before drawing
page.setColor (getBackground ());
page.fillRect (0, 0, width, height);
// Draw the barcode only if the UPC is valid
if (isValid ())
drawBarcode (page);
}
This method takes care of drawing the vertical slices over the label. //---------------------------------------------------------------------
// Draws the barcode if it is not null.
//---------------------------------------------------------------------
public void drawBarcode (Graphics page)
{
int barcodeLength;
// Set x to zero
x = 0;
if (barcode != null)
{
// Get the barcode length
barcodeLength = barcode.length ();
// Loop over every character (0 or 1)
for (int i = 0; i < barcodeLength; i++)
{
// Draw a white slice for the 0
if (barcode.charAt (i) == '0')
{
page.setColor (spaceColor);
}
// Draw a black slice for the '1'
else if (barcode.charAt (i) == '1')
{
page.setColor (barColor);
}
// Draw a red slice to show something graphically
else
{
page.setColor (errorColor);
}
// Update the coordinates
x += sliceWidth;
// Draw the slices at the specified coordinates
page.fillRect (x, 0, sliceWidth, height);
}
}
}
The UPC must be exactly 12 digits before we can generate the barcode from it. //---------------------------------------------------------------------
// Checks if the UPC is valid. a UPC is valid if it consists
// of exactly 12 digits.
//---------------------------------------------------------------------
public void validateUPC ()
{
valid = UPC.matches ("[0-9]{12}?");
}
The major method that generates the barcode from the UPC field. //---------------------------------------------------------------------
// Generate the barcode from the UPC...
//---------------------------------------------------------------------
public void generateBarcode ()
{
if (isValid ())
{
barcode = quiteZone + leftStartCode +
leftCodes[charToInteger (UPC.charAt (0))] +
leftCodes[charToInteger (UPC.charAt (1))] +
leftCodes[charToInteger (UPC.charAt (2))] +
leftCodes[charToInteger (UPC.charAt (3))] +
leftCodes[charToInteger (UPC.charAt (4))] +
leftCodes[charToInteger (UPC.charAt (5))] +
centerCode +
rightCodes[charToInteger (UPC.charAt (6))] +
rightCodes[charToInteger (UPC.charAt (7))] +
rightCodes[charToInteger (UPC.charAt (8))] +
rightCodes[charToInteger (UPC.charAt (9))] +
rightCodes[charToInteger (UPC.charAt (10))] +
rightCodes[charToInteger (UPC.charAt (11))] +
rightEndCode + quiteZone;
repaint ();
}
}
UPCField
MethodsCalls the parent ( //---------------------------------------------------------------------
// Create a field where user enters UPC code.
//---------------------------------------------------------------------
public UPCField (JButton MButton, JButton NButton)
{
// Create a 12 digit field having width of 8 cols
// M = 11, N = 12, MComponent = MButton, NComponent = NButton
// cols = 8.
"%22#MNDigitField"">super (11, 12, MButton, NButton, 8);
}
If the UPC is 11 digits long, then generate the check digit, and append it to the end of the UPC to form the 12 digit valid UPC. //---------------------------------------------------------------------
// Generates check digit and appends it to the upc field.
//---------------------------------------------------------------------
public void generateCheckDigit ()
{
// Generate check digit only if there are 11 characters
if (this.howManyDigits () == 11)
{
String upc = this.getText ();
int checksum = 0;
checksum = generateCheckDigit (upc);
// Append the check digit to the end of hte UPC
this.setText (upc + checksum);
}
}
Generates the check digit from the first 11 digits of the UPC field. //---------------------------------------------------------------------
// Generates check digit.
//---------------------------------------------------------------------
public int generateCheckDigit (String upc)
{
int checksum = 0;
for (int i = 1; i <= upc.length (); i++)
{
// even
if (i % 2 == 0)
checksum += charToInteger (upc.charAt (i - 1)) * 1;
// odd
else
checksum += charToInteger (upc.charAt (i - 1)) * 3;
}
return ( 10 - ( checksum % 10 ) ) % 10;
}
Tells whether the check digit is valid. //---------------------------------------------------------------------
// Makes sure that the check digit is valid.
//---------------------------------------------------------------------
public boolean isCheckDigitValid ()
{
if (howManyDigits () == 12)
{
String upc = this.getText ();
// The check digit entered by the user
// character 12 (start at 0)
int checkDigitEntered = charToInteger (upc.charAt (11));
// the generate (valid) check digit
int validCheckDigit = this.generateCheckDigit (upc.substring (0, 11));
return checkDigitEntered == validCheckDigit;
}
return false;
}
MNDigitField
This is a class that allows you to create a text field with only digits and with a maximum of public MNDigitField (int m, int n, JComponent mComp, JComponent nComp, int cols)
{
// Specify the width of the text field in terms of columns
super (cols);
// Set M and N
this.M = m;
this.N = n;
// create component actors
this.MComponentActor = new ComponentActor (mComp);
this.NComponentActor = new ComponentActor (nComp);
// Set the action type to ENABLE (This is default type, but I'm
// resetting to emphasize the idea)
this.MComponentActor.setActionType (ComponentActor.ENABLE);
this.NComponentActor.setActionType (ComponentActor.ENABLE);
// Add document listener to the text field
this.getDocument().addDocumentListener (new MNDocumentListener ());
}
The static class NDigitDocument extends PlainDocument
{
public void insertString (int offs, String str, AttributeSet a)
throws BadLocationException
{
if (str == null)
return;
// Don't allow number of characters to exceed N
// (The +1 is because insertString is called before insertupdate)
if (currentLength + 1 > N)
return;
// Validate that the characters of the string are digits
for (int i = 0; i < str.length(); i++)
{
if (!Character.isDigit (str.charAt (i)))
return;
}
super.insertString(offs, str, a);
}
}
The class MNDocumentListener implements DocumentListener
{
//-----------------------------------------------------------------
// Called when users insert characters.
//-----------------------------------------------------------------
public void insertUpdate (DocumentEvent e)
{
decide (e);
}
//-----------------------------------------------------------------
// Called when users remove characters (DEL or Back Space).
//-----------------------------------------------------------------
public void removeUpdate (DocumentEvent e)
{
decide (e);
}
//-----------------------------------------------------------------
// Do an action based on the values of M and N. In our case, the
// action is to enable or disable (reaction) the MComponent
// and NComponent.
//-----------------------------------------------------------------
public void decide (DocumentEvent e)
{
Document doc = (Document)e.getDocument();
currentLength = doc.getLength ();
// Enable MComponent if M digits reached
// Otherwise, disable it.
MComponentActor."%22#public">decide (currentLength == M);
// Enable NComponent if N digits reached
// Otherwise, disable it.
NComponentActor."%22#public">decide (currentLength == N);
}
}
Actor
This is an public void decide (boolean condition)
{
if (condition)
doAction ();
else
undoAction ();
}
ComponentActor
This class inherits from the The method called in our program to enable the 'Generate Check Digit' and 'Generate Bar Code' buttons is: //---------------------------------------------------------------------
// Perform an action.
//---------------------------------------------------------------------
public void doAction ()
{
// Do nothing if no component is supplied
if (component == null)
return;
// Perform action based on action type
switch (actionType)
{
case ENABLE:
component.setEnabled (true);
break;
case MAKE_VISIBLE:
component.setVisible (true);
break;
}
}
The method called in our program to disable the 'Generate Check Digit' and 'Generate Bar Code' buttons is: //---------------------------------------------------------------------
// Reverse the action..
//---------------------------------------------------------------------
public void undoAction ()
{
// Do nothing if no component is supplied
if (component == null)
return;
// Perform reaction based on action type
switch (actionType)
{
case ENABLE:
component.setEnabled (false);
break;
case MAKE_VISIBLE:
component.setVisible (false);
break;
}
}
ConclusionI created this program and wrote the article in 2005. I got it submitted to Code Project, but at that time Java was not supported on this site. I guess now I got back the opportunity to share it with the community. I hope it would be fun... Enjoy! Just in case you wondered what ASTI SPUMANTE means. It is a "semi-dry sparkling wine produced from the Moscato di Canelli grape in the village of Asti, in the Piedmont region of Italy". Revision History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||