|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article came about as a response to a request to extend the UPC-A barcode example to include EAN-8 and EAN-13. In this article, we will look at the EAN-13 specification and examine some code that can produce EAN-13 barcodes. EAN-13 BackgroundThe EAN-13 barcode is composed of 13 digits, which are made up of the following sections: the first 2 or 3 digits are the country code, the next 5 to 7 digits are the manufacturer code, the next 3 to 5 digits are the product code, and the last digit is the checksum digit. The figure below shows a typical EAN-13 barcode.
Country CodeThe Country Code is a 2 or 3 digit number, which is used to identify the country that assigned the manufacturer code. All EAN-13 barcodes that begin with "0" are UPC-A barcodes. The following table contains the Country Codes:
Manufacturer CodeThe EAN Manufacturer Code is a variable length number. Typically, 5 digit codes are assigned to companies, however, some companies do not produce enough products to warrant a 5 digit product code, and in such cases the EAN will issue Manufacturer Codes longer than 5 digits. Product CodeThe manufacturer is free to assign its own product codes, but they must make sure that each product code is unique within their product codes. The product codes can be as few as 3 digits long, or as long as 5 digits depending on the length of the country and manufacturer codes. Checksum DigitThe checksum digit is calculated using the country code, manufacturer's code, and the product code. The odd numbers starting with the right most digits are multiplied by 3 and added to the sum, while the even numbers are simply added to the sum. The reason for the EAN-13 check sum being calculated in reverse order (starting with the right most digit and considering it as being odd instead of even) is for compatibility with UPC-A barcodes. The modulus of 10 is then taken of the summed total. This is subtracted from 10, and the modulus of 10 is taken again.
Symbol SizeThe specifications for the EAN-13 barcode specify the nominal size as 37.29mm wide and 25.93mm high. Based upon this nominal size, the EAN-13 barcode can be scaled by a magnification factor of 0.8 to 2.0. Scaling the barcode will produce a barcode between the minimal allowable size of 29.83mm wide by 20.74mm high and the maximum allowable size of 74.58mm wide and 51.86mm high. Digit PatternsEach digit in a EAN-13 bar code is composed of a series of two spaces and two bars. Each digit is drawn within a space that is 7 modules wide. In addition to the 13 digits, which make up a EAN-13 barcode, the barcode symbol also has two quite zones, a lead block, a separator, and a trailing block. Each quite zone is 9 modules wide, the lead and trailing blocks are a series of lines and spaces in the format of bar, space, bar. The separator is signified by the sequence space / bar / space / bar / space.
In addition to the special symbol patterns listed above, the EAN-13 barcode symbol uses three distinct digit patterns as well, the Left Digit Odd Parity pattern, the Left Digit Even Parity pattern and the Right Digit pattern. The Left Digit patterns start with spaces, and the Right Digit pattern starts with bars (see table below).
where a '0' denotes a space and '1' represents a bar. The first digit of the Country Code is used to determine the parity of each digit of the Manufacturer's Code, see the Determining Number Parity section. The Right Digit pattern is typically used to draw the product code and the checksum digit. However, it will be used to render part of the manufacturer's code if the country code is greater than 2, or if the manufacturer's code is greater than 5. Determining Number ParityThe first digit of the Country Code in an EAN-13 barcode is not encoded, it is used to determine the parity of the digits in the manufacturer code. The second digit of the country code is always odd and the manufacturer's code will have three left-hand numbers that use even parity and two that use odd parity, except a UPC-A compatible barcode which uses all odd parity. The table below outlines the parity for the numbers in the Manufacturer's Code.
For example, if the country code is 75 then based upon the 1st country code digit and the above table, the 2nd digit of the country code, 5, would be Odd. The 1st digit of the Manufacturer Code would use the Even pattern, the 2nd digit would use the Odd pattern, the 3rd digit would use the Even pattern, the 4th would be Odd, and finally the 5th digit would use the Even pattern. Using the codeFirst, we will examine how to use the Using the Ean13 ClassThe code excerpt below uses the private void DrawEan13( )
{
System.Drawing.Graphics g = this.picBarcode.CreateGraphics( );
g.FillRectangle(
new System.Drawing.SolidBrush(System.Drawing.SystemColors.Control),
new Rectangle(0, 0, picBarcode.Width, picBarcode.Height));
// Create an instance of the Ean13 Class.
upc = new Ean13( );
upc.CountryCode = "12";
upc.ManufacturerCode = "34567";
upc.ProductCode = "89012";
upc.Scale =
(float)Convert.ToDecimal(cboScale.Items [cboScale.SelectedIndex]);
upc.DrawEan13Barcode( g, new System.Drawing.Point( 0, 0 ) );
g.Dispose( );
}
The first step for the The Ean13 ClassThe most significant variables are listed below: // This is the nomimal size recommended by the EAN.
private float _fWidth = 37.29f;
private float _fHeight = 25.93f;
private float _fFontSize = 8.0f;
private float _fScale = 1.0f;
// Left Hand Digits.
private string [] _aOddLeft = { "0001101", "0011001", "0010011", "0111101",
"0100011", "0110001", "0101111", "0111011",
"0110111", "0001011" };
private string [] _aEvenLeft = { "0100111", "0110011", "0011011", "0100001",
"0011101", "0111001", "0000101", "0010001",
"0001001", "0010111" };
// Right Hand Digits.
private string [] _aRight = { "1110010", "1100110", "1101100", "1000010",
"1011100", "1001110", "1010000", "1000100",
"1001000", "1110100" };
private string _sQuiteZone = "000000000";
private string _sLeadTail = "101";
private string _sSeparator = "01010";
The There are four primary functions which provide the majority of the functionality for the The first helper function public void CalculateChecksumDigit( )
{
string sTemp =
this.CountryCode + this.ManufacturerCode + this.ProductCode;
int iSum = 0;
int iDigit = 0;
// Calculate the checksum digit here.
for( int i = sTemp.Length; i >= 1; i-- )
{
iDigit = Convert.ToInt32( sTemp.Substring( i - 1, 1 ) );
// This appears to be backwards but the
// EAN-13 checksum must be calculated
// this way to be compatible with UPC-A.
if( i % 2 == 0 )
{ // odd
iSum += iDigit * 3;
}
else
{ // even
iSum += iDigit * 1;
}
}
int iCheckSum = ( 10 - ( iSum % 10 ) ) % 10;
this.ChecksumDigit = iCheckSum.ToString( );
}
The The second helper function used is the private string ConvertToDigitPatterns(string inputNumber, string [] patterns)
{
System.Text.StringBuilder sbTemp = new StringBuilder( );
int iIndex = 0;
for( int i = 0; i < inputNumber.Length; i++ )
{
iIndex = Convert.ToInt32( inputNumber.Substring( i, 1 ) );
sbTemp.Append( patterns[iIndex] );
}
return sbTemp.ToString( );
}
The
The The private string ConvertLeftPattern( string sLeft )
{
switch( sLeft.Substring( 0, 1 ) )
{
case "0":
return CountryCode0( sLeft.Substring( 1 ) );
case "1":
return CountryCode1( sLeft.Substring( 1 ) );
case "2":
return CountryCode2( sLeft.Substring( 1 ) );
case "3":
return CountryCode3( sLeft.Substring( 1 ) );
case "4":
return CountryCode4( sLeft.Substring( 1 ) );
case "5":
return CountryCode5( sLeft.Substring( 1 ) );
case "6":
return CountryCode6( sLeft.Substring( 1 ) );
case "7":
return CountryCode7( sLeft.Substring( 1 ) );
case "8":
return CountryCode8( sLeft.Substring( 1 ) );
case "9":
return CountryCode9( sLeft.Substring( 1 ) );
default:
return "";
}
}
Each country code has its own separate converter function. private string CountryCode1( string sLeft )
{
// 1 Odd Odd Even Odd Even Even
System.Text.StringBuilder sReturn = new StringBuilder( );
sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 0, 1 ),
this._aOddLeft ) );
sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 1, 1 ),
this._aOddLeft ) );
sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 2, 1 ),
this._aEvenLeft ) );
sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 3, 1 ),
this._aOddLeft ) );
sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 4, 1 ),
this._aEvenLeft ) );
sReturn.Append( ConvertToDigitPatterns( sLeft.Substring( 5, 1 ),
this._aEvenLeft ) );
return sReturn.ToString( );
}
The Finally the workhorse; the
This function begins by determining the width and height for the barcode by scaling the nominal width and height by the scale factor. The EAN-13 code - 1234567890128
To determine the total module width, simply add the individual parts: 9 + 3 + 7 + 35 + 5 + 35 + 7 + 3 + 9 = 113. The 2nd digit of the Country code, 2, will have an odd parity, the 1st digit of the manufacturer's code, 3, will use the Odd pattern, the 2nd digit, 4, will use the Even pattern, the 3rd digit, 5, would use the Odd pattern, the 4th and 5th digits, 6 and 7, would use the Even pattern. public void DrawEan13Barcode(System.Drawing.Graphics g, System.Drawing.Point pt)
{
float width = this.Width * this.Scale;
float height = this.Height * this.Scale;
// EAN13 Barcode should be a total of 113 modules wide.
float lineWidth = width / 113f;
// Save the GraphicsState.
System.Drawing.Drawing2D.GraphicsState gs = g.Save( );
// Set the PageUnit to Inch because all of
// our measurements are in inches.
g.PageUnit = System.Drawing.GraphicsUnit.Millimeter;
// Set the PageScale to 1, so a millimeter
// will represent a true millimeter.
g.PageScale = 1;
System.Drawing.SolidBrush brush =
new System.Drawing.SolidBrush(System.Drawing.Color.Black);
float xPosition = 0;
System.Text.StringBuilder strbEAN13 = new System.Text.StringBuilder( );
System.Text.StringBuilder sbTemp = new System.Text.StringBuilder( );
float xStart = pt.X;
float yStart = pt.Y;
float xEnd = 0;
System.Drawing.Font font =
new System.Drawing.Font("Arial", this._fFontSize * this.Scale);
// Calculate the Check Digit.
this.CalculateChecksumDigit( );
sbTemp.AppendFormat( "{0}{1}{2}{3}",
this.CountryCode,
this.ManufacturerCode,
this.ProductCode,
this.ChecksumDigit );
string sTemp = sbTemp.ToString( );
string sLeftPattern = "";
// Convert the left hand numbers.
sLeftPattern = ConvertLeftPattern(sTemp.Substring( 0, 7 ));
// Build the UPC Code.
strbEAN13.AppendFormat( "{0}{1}{2}{3}{4}{1}{0}",
this._sQuiteZone, this._sLeadTail,
sLeftPattern, this._sSeparator,
ConvertToDigitPatterns(sTemp.Substring( 7 ), this._aRight));
string sTempUPC = strbEAN13.ToString( );
float fTextHeight = g.MeasureString( sTempUPC, font ).Height;
// Draw the barcode lines.
for( int i = 0; i < strbEAN13.Length; i++ )
{
if( sTempUPC.Substring( i, 1 ) == "1" )
{
if( xStart == pt.X )
xStart = xPosition;
// Save room for the UPC number below the bar code.
if( ( i > 12 && i < 55 ) || ( i > 57 && i < 101 ) )
// Draw space for the number
g.FillRectangle( brush, xPosition, yStart,
lineWidth, height - fTextHeight );
else
// Draw a full line.
g.FillRectangle( brush, xPosition, yStart, lineWidth, height );
}
xPosition += lineWidth;
xEnd = xPosition;
}
// Draw the upc numbers below the line.
xPosition =
xStart - g.MeasureString(this.CountryCode.Substring( 0, 1 ), font).Width;
float yPosition = yStart + ( height - fTextHeight );
// Draw 1st digit of the country code.
g.DrawString( sTemp.Substring( 0, 1 ), font, brush,
new System.Drawing.PointF( xPosition, yPosition ) );
xPosition +=
(g.MeasureString(sTemp.Substring( 0, 1 ), font).Width + 43 * lineWidth) -
(g.MeasureString( sTemp.Substring( 1, 6 ), font ).Width);
// Draw MFG Number.
g.DrawString( sTemp.Substring( 1, 6 ), font, brush,
new System.Drawing.PointF( xPosition, yPosition ) );
xPosition +=
g.MeasureString(sTemp.Substring( 1, 6 ), font).Width + (11 * lineWidth);
// Draw Product ID.
g.DrawString( sTemp.Substring( 7 ), font, brush,
new System.Drawing.PointF( xPosition, yPosition ) );
// Restore the GraphicsState.
g.Restore( gs );
}
The function uses the The public System.Drawing.Bitmap CreateBitmap( )
{
float tempWidth = ( this.Width * this.Scale ) * 100 ;
float tempHeight = ( this.Height * this.Scale ) * 100;
System.Drawing.Bitmap bmp =
new System.Drawing.Bitmap( (int)tempWidth, (int)tempHeight );
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp);
this.DrawEan13Barcode( g, new System.Drawing.Point( 0, 0 ) );
g.Dispose( );
return bmp;
}
Special ThanksI would like to thank m@u for pointing out the flaw in my original article and source code. I would also like to thank MArmbruckner for testing version 2.0 and making sure the barcodes would scan properly. History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||