65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 绘制 UPC-A 条形码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (52投票s)

2005年4月6日

8分钟阅读

viewsIcon

278565

downloadIcon

12129

演示了使用 C# 绘制 UPC-A 条形码的方法。

引言

在几乎所有售出的商品上,通常都有某种 UPC 条形码用于识别商品。在美国和加拿大最常用的条形码是 UPC-A 条形码。在本文中,我们将探讨 UPC-A 规范并研究一些可以生成 UPC-A 条形码的代码。

UPC-A 背景

UPC-A 条形码由 12 位数字组成,这些数字包含以下部分:

  • 第一位数字是产品类型;
  • 接下来的五位数字是制造商代码;
  • 接下来的五位数字是产品代码;
  • 最后一位数字是校验和位。

产品类型

产品类型是一个数字,用于描述产品的类型。

产品类型编号 描述
0 普通 UPC 代码
1 保留
2 商店标记的重量商品。
3 国家药品/健康相关代码。
4 无格式限制,商店内用于非食品项目。
5 优惠券
6 保留
7 普通 UPC 代码
8 保留
9 保留

制造商代码和产品代码

制造商代码由统一代码委员会分配,用于唯一标识产品的制造商。产品代码用于标识产品。

校验和位

校验和位是根据产品类型、制造商代码和产品代码计算得出的。奇数位乘以 3 并加到总和中,而偶数位则直接加到总和中。然后取总和的 10 的模。将其从 10 中减去,然后再次取 10 的模。

例如:UPC-A 01234567890
产品类型:0
制造商代码:12345
产品代码:67890

第一个数字 '0' 是奇数,所以乘以 3,第二个数字 1 是偶数,所以直接相加,依此类推……

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

85 % 10 = 5

( ( 10 - 5 ) % 10 ) = 5

符号尺寸

UPC-A 条形码的规格规定了 UPC 符号的标称尺寸为 1.496 英寸宽,1.02 英寸高。基于此标称尺寸,UPC 符号可以以 0.8 到 2.0 的放大因子进行缩放。缩放条形码将生成介于最小允许尺寸 1.175 英寸宽 x 0.816 英寸高和最大允许尺寸 2.938 英寸宽 x 2.04 英寸高之间的条形码。

数字模式

UPC-A 条形码中的每个数字由一系列两个空格和两个条组成。每个数字绘制在一个 7 个模块宽的空间内。除了构成 UPC-A 条形码的 12 个数字之外,条形码符号还有两个安静区、一个前导块、一个分隔符和一个尾随块。每个安静区宽 9 个模块,前导块和尾随块是一系列线和空格,格式为条、空格、条。分隔符由序列空格/条/空格/条/空格表示。

特殊符号 模式
安静区 000000000
前导/尾随 101
分隔符 01010

其中 '0' 表示空格,'1' 表示条。

除了上面列出的特殊符号模式外,UPC-A 条形码符号还使用了两种不同的数字模式:左数字模式和右数字模式。左数字模式用于绘制产品类型和制造商代码。右数字模式用于绘制产品代码和校验和位。左数字模式以空格开头,而右数字模式以条开头(请参见下表)。

数字 左数字 右数字
0 0001101 1110010
1 0011001 1100110
2 0010011 1101100
3 0111101 1000010
4 0100011 1011100
5 0110001 1001110
6 0101111 1010000
7 0111011 1000100
8 0110111 1001000
9 0001011 1110100

其中 '0' 表示空格,'1' 表示条。

使用代码

首先,我们将研究如何使用 UpcA 类,然后我们将研究 UpcA 类的工作原理。

使用 UpcA 类

下面的代码片段使用 UpcA 类在 PictureBox 控件中绘制 UPC-A 条形码。

private void DrawUPC( )
{        
   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 UpcA Class.        
   upc = new UpcA( ); 
    
   upc.ProductType = "0";
   upc.ManufacturerCode = "21200"; 
   upc.ProductCode = "10384"; 
   upc.Scale = 
     (float)Convert.ToDecimal( cboScale.Items [cboScale.SelectedIndex] ); 
    
   upc.DrawUpcaBarcode( g, new System.Drawing.Point( 0, 0 ) ); 
    
   g.Dispose( );
}

DrawUPC 函数的第一步是创建 UpcA 类的实例,然后设置产品类型、制造商代码、产品代码和比例因子属性(校验和将由 UpcA 类计算)。设置完这些属性后,调用 DrawUpcaBarcode 函数,传递一个 Graphics 对象和一个 Point,该点指示开始绘制的位置,这将导致条形码从点 (0, 0) 开始在 PictureBox 中绘制。

UpcA 类

最重要的变量列在下面。

// This is the nomimal size recommended by the UCC.
private float _fWidth = 1.469f;
private float _fHeight = 1.02f;
private float _fFontSize = 8.0f;
private float _fScale = 1.0f;

// Left Hand Digits.
private string [] _aLeft = { "0001101", "0011001", "0010011", "0111101", 
                             "0100011", "0110001", "0101111", "0111011", 
                             "0110111", "0001011" };

// Right Hand Digits.
private string [] _aRight = { "1110010", "1100110", "1101100", "1000010", 
                              "1011100", "1001110", "1010000", "1000100", 
                              "1001000", "1110100" };

private string _sQuiteZone = "0000000000";
private string _sLeadTail = "101";
private string _sSeparator = "01010";

_fWidth_fHeight_fScale 变量使用统一代码委员会推荐的标称尺寸进行初始化。渲染条形码时,其实际尺寸将由标称尺寸和比例因子决定,如本文的“符号尺寸”部分所述。变量 _aLeft_aRight_sQuiteZone_sLeadTail_sSeparator 都是表示 UPC-A 条形码各个部分的条/空格图形的字符串表示。本质上,'1' 表示条,'0' 表示空格,因此 _sSeparator 将导致渲染空格-条-空格-条-空格。使用 string 的替代方法是使用二进制表示,其中 0 位代表空格,1 位代表条。

UpcA 类提供了三个主要功能,提供了大部分功能。这些功能的主力是 DrawUpcaBarcodeDrawUpcaBarcode 使用其他两个函数作为辅助函数。两个辅助函数是:CalculateChecksumDigitConvertToDigitPatterns,它们将首先进行讨论。还有一个第四个函数 CreateBitmap,它提供了一种创建位图图像的简单方法。

第一个辅助函数 DrawUpcaBarcode 调用 CalculateChecksumDigit 函数,该函数使用产品类型、制造商代码和产品代码来计算条形码的校验和。

public void CalculateChecksumDigit( )
{
    string sTemp = this.ProductType + this.ManufacturerCode + this.ProductCode;
    int iSum = 0;
    int iDigit = 0;

    // Calculate the checksum digit here.
    for( int i = 1; i <= sTemp.Length; i++ )
    {
        iDigit = Convert.ToInt32( sTemp.Substring( i - 1, 1 ) );
        if( i % 2 == 0 )
        {    // even
            iSum += iDigit * 1;
        }
        else
        {    // odd
            iSum += iDigit * 3;
        }
    }

    int iCheckSum = ( 10 - ( iSum % 10 )  ) % 10;
    this.ChecksumDigit = iCheckSum.ToString( );

}

CalculateChecksumDigit 函数使用本文“校验和位”部分讨论的方法计算校验和。

第二个辅助函数是 ConvertToDigitPatterns 函数。此函数接受制造商代码和产品编号的各个数字,并将它们转换为条形码图形的字符串表示。

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( );
}

ConvertToDigitPatterns 函数需要两个参数:

  • inputNumber
  • 模式

inputNumber 将是制造商编号或产品编号,而 patterns 将是 _aLeft_aRight 数组,具体取决于 inputNumber 是制造商编号还是产品编号。

最后,主力函数;DrawUpcaBarcode 处理条形码图形的渲染,需要两个参数:

  • g
  • pt

此函数首先通过将标称宽度和高度乘以比例因子来确定条形码的宽度和高度。lineWidth 基于渲染 UPC-A 条形码所需的总模块数。总模块数 113 是通过以下方式确定的:例如

UPC-A 代码 - 021900103841

条形码部分 数值 图形表示 模块数
安静区 N/A 000000000 9 个模块
潜在客户 N/A 101 3 个模块
产品类型 1 位数字 - "0" 0001101 7 个模块
制造商编号 5 位数字 = "21900" 00100110011001000101100011010001101 5 位数字 * 7 个模块 = 35 个模块
分隔符 N/A 01010 5 个模块
产品编号 5 位数字 = "10384" 11001101110010100001010010001011100 5 位数字 * 7 个模块 = 35 个模块
校验和 1 位数字 = "1" 1100110 7 个模块
尾随 N/A 101 3 个模块
安静区 N/A 000000000 9 个模块

因此,要确定总模块宽度,只需将各个部分相加:9 + 3 + 7 + 35 + 5 + 35 + 7 + 3 + 9 = 113。

public void DrawUpcaBarcode(System.Drawing.Graphics g,System.Drawing.Point pt)
{

    float width = this.Width * this.Scale;
    float height = this.Height * this.Scale;

    // A upc-a excluding 2 or 5 digit supplement information 
    // should be a total of 113 modules wide. 
    // Supplement information is typically 
    // used for periodicals and books.
    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.Inch;

    // Set the PageScale to 1, so an inch will represent a true inch.
    g.PageScale = 1;

    System.Drawing.SolidBrush brush = 
           new System.Drawing.SolidBrush( System.Drawing.Color.Black );

    float xPosition = 0;

    System.Text.StringBuilder strbUPC = 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( );

    // Build the UPC Code.
    strbUPC.AppendFormat( "{0}{1}{2}{3}{4}{5}{6}{1}{0}",
                this._sQuiteZone, this._sLeadTail,
                ConvertToDigitPatterns( this.ProductType, this._aLeft ),
                ConvertToDigitPatterns( this.ManufacturerCode, this._aLeft ),
                this._sSeparator,
                ConvertToDigitPatterns( this.ProductCode, this._aRight ), 
                ConvertToDigitPatterns( this.ChecksumDigit, this._aRight ) );

    string sTempUPC = strbUPC.ToString( );

    float fTextHeight = g.MeasureString( sTempUPC, font ).Height;

    // Draw the barcode lines.
    for( int i = 0; i < strbUPC.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 > 19 && i < 56 ) || ( i > 59 && i < 95 ) )
                // 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.ProductType, font ).Width;
    float yPosition = yStart + ( height - fTextHeight );
    
    // Draw Product Type.
    g.DrawString( this.ProductType, font, brush, 
                   new System.Drawing.PointF( xPosition, yPosition ) );

    // Each digit is 7 modules wide, therefore the MFG_Number 
    // is 5 digits wide so
    // 5 * 7 = 35, then add 3 for the LeadTrailer 
    // Info and another 7 for good measure,
    // that is where the 45 comes from.
    xPosition += 
          g.MeasureString( this.ProductType, font ).Width + 45 * lineWidth - 
                     g.MeasureString( this.ManufacturerCode, font ).Width;

    // Draw MFG Number.
    g.DrawString( this.ManufacturerCode, font, brush, 
                         new System.Drawing.PointF( xPosition, yPosition ) );

    // Add the width of the MFG Number and 5 modules for the separator.
    xPosition += g.MeasureString( this.ManufacturerCode, font ).Width +
                        5 * lineWidth;

    // Draw Product ID.
    g.DrawString( this.ProductCode, font, brush, 
                          new System.Drawing.PointF( xPosition, yPosition ) );

    // Each digit is 7 modules wide, therefore 
    // the Product Id is 5 digits wide so
    // 5 * 7 = 35, then add 3 for the LeadTrailer 
    // Info, + 8 more just for spacing
    // that is where the 46 comes from.
    xPosition += 46 * lineWidth;

    // Draw Check Digit.
    g.DrawString( this.ChecksumDigit, font, brush, 
                          new System.Drawing.PointF( xPosition, yPosition ) );

    // Restore the GraphicsState.
    g.Restore( gs );

}

该函数使用 CalculateChecksumDigit 函数计算正确的校验和位,然后使用 ConvertToDigitPatterns 函数将 UPC-A 条形码数字的各个部分转换为字符串表示。一旦数字被转换为字符串表示,代码就会使用字符串表示来渲染条形码,1 将导致绘制一个矩形,0 将导致代码跳过绘制矩形。如果代码绘制了矩形,它还会考虑是否需要缩短矩形以留出制造商编号和产品编号的空间。条形码完全渲染后,代码将确定位置,并绘制产品类型编号、制造商编号、产品编号和校验和位。

CreateBitmap 函数仅创建一个 Bitmap 对象,并使用 DrawUpcaBarcode 函数将条形码渲染到 Bitmap 对象,然后返回 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.DrawUpcaBarcode( g, new System.Drawing.Point( 0, 0 ) );
    g.Dispose( );
    return bmp;
}

关注点

美国和加拿大是仅有的两个使用 UPC-A 条形码系统的国家,世界其他地区使用 EAN 条形码系统。因此,自 2005 年 1 月 1 日起,统一代码委员会 (UCC) 已强制要求所有美国和加拿大的销售点公司都必须能够扫描和处理 EAN-8 和 EAN-13 条形码,以及 UPC-A 条形码(这被称为 2005 年日出)。UCC 还开始推广一种新的条形码系统,称为全球贸易项目编号 (GTIN),基本上 GTIN 是一个 14 位数字,符合 UPC-A 和 EAN-13 符号标准,但使用额外的数字来存储原产国信息。如果您想了解更多信息,请访问 UCC: 2005 Sunrise

历史

  • 版本 1.0 - 初始应用程序。
© . All rights reserved.