Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating EAN-13 Barcodes with C#

0.00/5 (No votes)
11 May 2005 11  
Demonstrates creating EAN-13 Barcodes with C#.

Introduction

This 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 Background

The 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 Code

The 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:

00-13: USA & Canada 20-29: In-Store Functions 30-37: France
40-44: Germany 45: Japan (also 49) 46: Russian Federation
471: Taiwan 474: Estonia 475: Latvia
477: Lithuania 479: Sri Lanka 480: Philippines
482: Ukraine 484: Moldova 485: Armenia
486: Georgia 487: Kazakhstan 489: Hong Kong
49: Japan (JAN-13) 50: United Kingdom 520: Greece
528: Lebanon 529: Cyprus 531: Macedonia
535: Malta 539: Ireland 54: Belgium & Luxembourg
560: Portugal 569: Iceland 57: Denmark
590: Poland 594: Romania 599: Hungary
600 & 601: South Africa 609: Mauritius 611: Morocco
613: Algeria 619: Tunisia 622: Egypt
625: Jordan 626: Iran 64: Finland
690-692: China 70: Norway 729: Israel
73: Sweden 740: Guatemala 741: El Salvador
742: Honduras 743: Nicaragua 744: Costa Rica
746: Dominican Republic 750: Mexico 759: Venezuela
76: Switzerland 770: Colombia 773: Uruguay
775: Peru 777: Bolivia 779: Argentina
780: Chile 784: Paraguay 785: Peru
786: Ecuador 789: Brazil 80 - 83: Italy
84: Spain 850: Cuba 858: Slovakia
859: Czech Republic 860: Yugloslavia 869: Turkey
87: Netherlands 880: South Korea 885: Thailand
888: Singapore 890: India 893: Vietnam
899: Indonesia 90 & 91: Austria 93: Australia
94: New Zealand 955: Malaysia 977: International Standard Serial Number for Periodicals (ISSN)
978: International Standard Book Numbering (ISBN) 979: International Standard Music Number (ISMN) 980: Refund receipts
981 & 982: Common Currency Coupons 99: Coupons

Manufacturer Code

The 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 Code

The 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 Digit

The 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.

For example: EAN-13 001234567890
Country Code : 00
Manufacturer's Code : 12345
Product Code : 67890

The last or right most digit is '0' and is considered odd, so multiply it by 3, the second right most digit '9' is even so just add it, etc...

(0 * 3) + 9 + (8 * 3) + 7 + (6 * 3) + 5 + (4 * 3) + 3 + (2 * 3) + 1 + (0 * 3) + 0 = 85

85 % 10 = 5

( ( 10 - 5 ) % 10 ) = 5

Symbol Size

The 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 Patterns

Each 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.

Special Symbol Pattern
Quite Zone 000000000
Lead / Trailer 101
Separator 01010

where '0' represents space and '1' denotes a bar.

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).

Number Left Digits Right Digits
Odd Parity Even Parity
0 0001101 0100111 1110010
1 0011001 0110011 1100110
2 0010011 0011011 1101100
3 0111101 0100001 1000010
4 0100011 0011101 1011100
5 0110001 0111001 1001110
6 0101111 0000101 1010000
7 0111011 0010001 1000100
8 0110111 0001001 1001000
9 0001011 0010111 1110100

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 Parity

The 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.

First Country Code Digit Parity
Second Country Code Digit Manufacturer Code Digits
1 2 3 4 5
0 (UPC-A) Odd Odd Odd Odd Odd Odd
1 Odd Odd Even Odd Even Even
2 Odd Odd Even Even Odd Even
3 Odd Odd Even Even Even Odd
4 Odd Even Odd Odd Even Even
5 Odd Even Even Odd Odd Even
6 Odd Even Even Even Odd Odd
7 Odd Even Odd Even Odd Even
8 Odd Even Odd Even Even Odd
9 Odd Even Even Odd Even Odd

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 code

First, we will examine how to use the Ean13 class, and then we'll examine how the Ean13 class works.

Using the Ean13 Class

The code excerpt below uses the Ean13 class to draw a EAN-13 barcode in a picture box control:

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 DrawEan13 function is to create an instance of the Ean13 class, and then set the country code, manufacturer code, the product code, and the scale factor properties (the check sum will be calculated by the Ean13 class). Once these properties are set, a call to the DrawEan13Barcode function is made, passing a Graphics object and a Point, which indicates the starting position to draw at, this will cause the barcode to be drawn in the picture box starting at point (0, 0).

The Ean13 Class

The 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 _fWidth, _fHeight, and the _fScale variables are initialized with the nominal size. When the barcode is rendered, its actual size will be determined by the nominal size, and the scale factor, as discussed in the Symbol Size section of this article. The variables _aOddLeft, _aEvenLeft, _aRight, _sQuiteZone, _sLeadTail, and _sSeparator are all string representations of the bar/space graphics, which represent the various parts of an EAN-13 barcode. Essentially, a '1' represents a bar and a '0' represents a space, so _sSeparator would cause a space-bar-space-bar-space to be rendered. An alternate method to using a string could be to use a binary representation, where a 0 bit would be a space and a 1 bit is a bar.

There are four primary functions which provide the majority of the functionality for the Ean13 class. The workhorse of these functions is DrawEan13Barcode, which uses several functions as helper functions. The helper functions are: CalculateChecksumDigit, ConvertToDigitPatterns, ConvertLeftPattern which will be discussed first. There is also a fifth function, CreateBitmap, which provides an easy means for creating a bitmap image.

The first helper function DrawEan13Barcode calls the CalculateChecksumDigit function, which uses the country code, manufacturer code, and product code to calculate the barcode's check sum.

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 CalculateChecksumDigit function calculates the check sum using the method discussed in the Checksum Digit section listed above.

The second helper function used is the ConvertToDigitPatterns function. This function takes the individual numbers of the manufacturer code, and the product number, and converts them to the string representation of the barcode graphics.

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 ConvertToDigitPatterns function requires two parameters:

  • inputNumber
  • patterns

The inputNumber will be either the manufacturer number, or the product number, and the patterns will be the _aOddLeft, _aEvenLeft, or the _aRight array depending on whether the inputNumber is the manufacturer number or the product number.

The ConvertLeftPatterns is used to create the left hand patterns discussed in the Determining Number Parity section. The ConvertLeftPatterns determines the country code, and calls the appropriate country code converter.

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. CountryCode0 is a UPC-A barcode so it will use only the left hand odd parity patterns. CountryCode1 through CountryCode9 each uses the appropriate parity patterns discussed in the Determining Number Parity section. See the CountryCode1 code below for an example of a country code 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 CountryCode1 function uses the parity patterns discussed in the Determining Number Parity section to send each number to the ConvertToDigitPatterns function with the correct left hand pattern. The sLeft parameter should contain the 2nd digit of the Country Code and the Manufacturer's Code.

Finally the workhorse; the DrawEan13Barcode handles the rendering of the barcode graphics and requires two parameters:

  • g
  • pt

This function begins by determining the width and height for the barcode by scaling the nominal width and height by the scale factor. The lineWidth is based upon the total number of modules required to render an EAN-13 barcode. The total number of modules, 113, is determined by the following: for example:

EAN-13 code - 1234567890128

Barcode Section Numeric Value Graphic Representation Number of Modules
Quite Zone N/A 000000000 9 modules
Lead N/A 101 3 modules
1st Digit of Country Code 1 digit - "1" Used to determine the parity.
2nd Digit of Country Code 1 digit - "2" 0010011 7 modules
Manufacturer Number 5 digits = "34567" 01111010011101011000100001010010001 5 digits * 7 modules = 35 modules
Separator N/A 01010 5 modules
Product Number 5 digits = "89012" 10010001110100111001011001101101100 5 digits * 7 modules = 35 modules
Check Sum 1 digit = "8" 1001000 7 modules
Trailer N/A 101 3 modules
Quite Zone N/A 000000000 9 modules

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 CalculateChecksumDigit function to calculate the correct check sum digit, next the call to ConvertLeftPatterns is made, and then the ConvertToDigitPatterns function is used to convert the product code and check sum number of the EAN-13 barcode number to a string representation. Once the number has been converted over to a string representation, the code uses the string representation to render the barcode, 1 will cause a rectangle to be drawn, and 0 will cause the code to skip drawing a rectangle. If the code draws a rectangle, it also takes into consideration whether it needs to shorten the rectangle to allow space for the manufacturer's number and the product number. Once the barcode is completely rendered, the code then determines the position, and draws the country code, the manufacturer's number, the product number, and the check sum digit.

The CreateBitmap function simply creates a Bitmap object, and uses the DrawEan13Barcode function to render the barcode to the Bitmap object, and then it returns the Bitmap.

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 Thanks

I 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

  • Version 1.0 - Initial application.
  • Version 2.0 - Revised application to use the correct barcode patterns based on the 1st digit of the country code.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here