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

使用 C# 绘制 POSTNET 条形码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (14投票s)

2004年12月6日

CPOL

6分钟阅读

viewsIcon

106172

downloadIcon

2326

本文演示了一种绘制 POSTNET 条形码的方法。

app400x200.gif

引言

本文研究了 POSTNET 条形码系统,并演示了使用 C# 2005 和 GDI+ 创建 POSTNET 条形码的一些基本功能。

背景

如今,许多雇主都相当节俭,因此让他们购买像邮政条形码字体这样的简单东西有时会非常困难。曾有过几次这样的经历,我决定研究邮政条形码系统,并创建一个简单的库,在给定邮政编码的情况下能够绘制 POSTNET 条形码。

POSTNET 条形码概述

POSTNET 条形码可以包含几个信息片段:起始/结束码、邮政编码、+4 码、投递点码和校验码(见下图)。

Description of POSTNET Barcode format.

起始/结束码标志着条形码的开始和结束。起始/结束码是强制性的,由一个单独的“全条”组成。邮政编码也是强制性的,由 25 个条组成。“+4”码由 20 个条组成,是可选的。投递点码由 10 个条组成,是可选的,通常是街道地址、邮政信箱、乡村路线号或公路合同路线号的最后两位数字。校验码是强制性的,基本上是一个校验和(我在本文中可互换地使用校验码和校验和这两个术语)。

POSTNET Barcode Numbers.

每个数字都由一个由五个条组成的独特图案表示(左侧图片显示了图案)。基本上,有两种不同的条类型:“全条”和“半条”。“全条”的高度必须在 0.115 英寸到 0.135 英寸之间。“半条”的高度必须在 0.04 英寸到 0.06 英寸之间。全条和半条的宽度都必须在 0.015 英寸到 0.025 英寸之间。

除了高度和宽度规格外,条之间还有间距要求;它必须在 0.012 英寸到 0.040 英寸之间。

POSTNET 条形码规格
Specification 标称值 允许的偏差
全条高度 0.125" +/-0.01"
半条高度 0.05" +/-0.01"
条宽 0.02" +/-0.005"
条间距 0.026" +/-0.014
中心线间距 0.0458" +/-0.0042"

(如需完整参考,请参阅美国邮政服务局提供的 POSTNET 条形码规格,可在此 获取,或搜索 POSTNET 条形码规格。)

校验码

校验码是一个简单的校验和,通过将邮政编码中的所有数字相加,然后对总和取模 10,最后用 10 减去该结果来计算。

示例:邮政编码 + 4 位是 12345-6789,投递点是 01。

1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 = 46

46 % 10 = 6

10 - 6 = 4

4 % 10 = 4

所以校验码是 4;最后的模运算用于总和邮政编码是 10 的倍数的特殊情况。

代码

Postnet 类用于使用 Graphics 对象句柄来渲染条形码,或创建 Bitmap 类。唯一必需的数据是邮政编码;+4 位和投递点是可选的。校验码将由 Postnet 类计算。

Using the Code

Postnet 类将处理渲染条形码的所有方面。只需创建 Postnet 类的实例,并将邮政编码作为参数传递给构造函数。然后,调用 DrawPOSTNET 函数,并将 Graphics 对象和起始点作为参数传递。处理完成后,执行任何其他绘图任务,然后通过调用 Graphics 对象的 Dispose 函数进行清理。

    // Create an instance of the Postnet class and send it
    //  the zip code, +4, and delivery point.  The Postnet class will
    //  calculate the correction character.
    Postnet ps = new Postnet( "12345", "6789", "01" );
    
    // Create a Graphics object handle from a picture box.
    System.Drawing.Graphics g = this.pictureBox1.CreateGraphics( );
    
    // Send a handle to the Graphics object and the starting
    //  point to begin drawing the barcode.
    ps.DrawPOSTNET( g, new System.Drawing.Point( 0, 0 ) );

    // Handle any other drawing necessary.
    
    // Clean up.
    g.Dispose( );

Postnet 类提供的第二个功能是以位图形式创建条形码。要创建位图,请创建 Postnet 类的实例,并将邮政编码传递给构造函数。然后,调用 CreateBitmap 函数,并将要创建的位图的宽度和高度作为参数传递。

    // Create an instance of the Postnet class and send it
    //  the zip code, +4, and delivery point.  The Postnet class will
    //  calculate the correction character.
    Postnet ps = new Postnet( "12345", "6789", "01" );
    
    // Call the CreateBitmap function passing in the width and height for the
    //  newly created bitmap.
    pictureBox1.Image = ps.CreateBitmap( this.pictureBox1.Width, 
                                         this.pictureBox1.Height );

代码工作原理

Postnet 类相对简单。它提供了几种用于不同创建方式的构造函数;几个用于设置和获取类数据的属性/特性;上面讨论的两个公共函数,以及一些处理繁重工作的私有函数。

创建类的实例时,它会通过调用 InitBarSpecification 函数来初始化条形码规格。此函数设置多项信息,这些信息控制条形码的各个方面,并初始化 DRAWING_INFO 结构(见下面的代码)。

        private void InitBarSpecifications( )
        {
            // Set the Bar specifications with the default values.
            di = new DRAWING_INFO( );
            _fBarWidth = .022f;
            _fTallBarHeight = 0.125f;
            _fShortBarHeight = 0.05f;
            _fBarSpacing = 0.02f;
        }

DRAWING_INFO 包含用于绘制条形码的画笔实例、当前 x 位置和当前 y 位置。

        private struct DRAWING_INFO
        {
            public System.Drawing.SolidBrush brush;
            public float xStart;
            public float yStart;
        }

DrawPOSTNET 函数使用 CalculateCorrectionCharacter 函数来计算校验和,渲染起始/结束码,并调用 DrawBars 函数来渲染邮政编码、+4 码、投递点和校验码。除了这些功能外,DrawPOSTNET 函数还会保存 Graphics 对象的 GraphicsState,并在完成条形码绘制后恢复它。一个有趣的项目是使用 g.PageUnitg.PageScale;如果这些未设置或未正确获取,条形码可能会过大或过小。PageUnitPageScale 用于自动将渲染从像素调整为英寸。PageUnit 是一个 System.Drawing.GraphicsUnit 枚举类型,它告诉 GDI+ 对象将绘图转换为哪种单位。PageScale 用于缩放渲染;因此,如果 PageScale 设置为 .5,则绘图中的一英寸将按比例缩小到实际设备(显示器、打印机等)上的半英寸。

        public void DrawPOSTNET( System.Drawing.Graphics g, 
                                 System.Drawing.Point startPt )
        {
            // Calculate the Check Sum.
            CalculateCorrectionCharacter( );

            // Save the user's graphics state, just in case they are doing
            //  something special.
            System.Drawing.Drawing2D.GraphicsState gs = g.Save( );

            // Failure to set the PageUnit and PageScale will result
            //    in odd results.
            g.PageUnit = System.Drawing.GraphicsUnit.Inch;
            g.PageScale = 1f;

            di.brush = new System.Drawing.SolidBrush( System.Drawing.Color.Black );
            di.xStart = startPt.X;
            di.yStart = startPt.Y;

            // Print Frame Bar
            g.FillRectangle( di.brush, di.xStart, di.yStart, 
                             _fBarWidth, _fTallBarHeight );
            di.xStart += ( _fBarWidth + _fBarSpacing );

            // Print Zip Code
            string sTempValue = ZipCode.ToString( );
            DrawBars( g, sTempValue, ref di );

            // Print Plus4
            if( Plus4.Length == 4 )
            {
                sTempValue = Plus4.ToString( );
                DrawBars( g, sTempValue, ref di );

                // Print Delivery Point
                if( this.DeliveryPoint.Length > 0 && this.DeliveryPoint.Length < 3 )
                {
                    sTempValue = DeliveryPoint.ToString( );
                    DrawBars( g, sTempValue, ref di );
                }
            }
            
            // Print Correction Character
            sTempValue = this.CorrectionCharacter.ToString( );
            DrawBars( g, sTempValue, ref di );

            // Print Frame Bar
            g.FillRectangle( di.brush, di.xStart, di.yStart, 
                             _fBarWidth, _fTallBarHeight );

            // Restore the saved user's graphics state, just incase they are doing
            //  something special.
            g.Restore( gs );
        }

CalculateCorrectionCharacter 函数简单地将邮政编码、+4 码和投递点码中的数字相加;然后,它将总和的数字取模 10;从 10 中减去该数字,然后再次取模 10。

         private void CalculateCorrectionCharacter( )
        {
            string sZipCode;
            sZipCode = ZipCode.ToString( );

            int iCorrectionCharacter = 0;

            for( int i = 0; i < sZipCode.Length; i++ )
            {
                iCorrectionCharacter += 
                             Convert.ToInt32( sZipCode.Substring( i, 1 ) );
            }

            if( this.Plus4.Length == 4 )
            {
                string sPlus4 = Plus4.ToString( );
                for( int i = 0; i < sPlus4.Length; i++ )
                {
                    iCorrectionCharacter += 
                             Convert.ToInt32( sPlus4.Substring( i, 1 ) );
                }

                if( this.DeliveryPoint.Length > 0 
                                      && this.DeliveryPoint.Length < 3 )
                {
                    string sDeliveryPoint = this.DeliveryPoint.ToString( );
                    for( int i = 0; i < sDeliveryPoint.Length; i++ )
                    {
                        iCorrectionCharacter += 
                             Convert.ToInt32( sDeliveryPoint.Substring( i, 1 ) );
                    }
                }
            }
            int iLeftOver = iCorrectionCharacter % 10;
            this._iCorrectionCharacter = ( 10 - iLeftOver ) % 10;
        }

DrawBars 函数使用 sNumber 参数和 _aBarcodeValues 数组来确定条形码的绘制方式。_aBarcodeValues 数组包含数字格式;0 是短条,1 是长条。

         string [] _aBarcodeValues = {    "11000",    // 0
                        "00011",    // 1
                        "00101",    // 2
                        "00110",    // 3
                        "01001",    // 4
                        "01010",    // 5
                        "01100",    // 6
                        "10001",    // 7
                        "10010",    // 8
                        "10100" };  // 9

        ...
     
        private void DrawBars( System.Drawing.Graphics g, 
                     string sNumber, ref DRAWING_INFO di )
        {
            string sTempValue;
            for( int i = 0; i < sNumber.Length; i++ )
            {
                sTempValue = 
                  _aBarcodeValues[Convert.ToInt32( sNumber.Substring( i, 1 ) )];
                for( int j = 0; j < sTempValue.Length; j++ )
                {
                    if( sTempValue.Substring( j, 1 ) == "0" )
                        g.FillRectangle( di.brush, di.xStart, di.yStart + 
                        ( _fTallBarHeight - _fShortBarHeight ), 
                                            _fBarWidth, _fShortBarHeight );
                    else
                        g.FillRectangle( di.brush, di.xStart, di.yStart, 
                                         _fBarWidth, _fTallBarHeight );

                    di.xStart += ( _fBarWidth + _fBarSpacing );
                }
            }
        }

最后一个函数是 CreateBitmap 函数。此函数接受所需的宽度和高度来创建新的位图。它从 Bitmap 创建一个 Graphics 对象,绘制条形码,然后返回 Bitmap 对象。

        public System.Drawing.Bitmap CreateBitmap( int width, int height )
        {
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap( width, height );

            System.Drawing.Graphics g = System.Drawing.Graphics.FromImage( bmp );
            this.DrawPOSTNET( g, new System.Drawing.Point( 0, 0 ) );
            g.Dispose( );
            return bmp;

        }

完成了!

好了,这就是 Postnet 类的内容了,我probably 应该回去工作了。虽然这可能不是一个热门话题,但我确实希望它能帮助到一些人,至少是通过分享一些知识。

历史

  • 版本 1.0.0 - 2004 年 12 月 1 日 - 初始版本。
  • 版本 1.1.0 - 2004 年 12 月 7 日 - 修正了校验码计算 - 感谢 je_gonzalez。
© . All rights reserved.