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

在 Windows 中绘制条形码第三部分 - I2of5

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (9投票s)

2002 年 6 月 15 日

BSD

7分钟阅读

viewsIcon

98350

downloadIcon

198

一篇关于将 I2of5 条形码绘制到屏幕或剪贴板的文章。

引言

我最近在工作中有一个项目,要求我将条形码字符写入一个字体文件中,供老式照排机使用。这段经历启发我开始了一个副项目,编写一些代码,在给定适当输入的情况下,可以在 Windows 屏幕上渲染条形码。这一系列文章就是该项目的成果。

I2of5 基础知识

本篇(第三篇)文章将介绍如何在 Windows 屏幕上绘制 I2of5 条形码。在开始讨论代码之前,我们需要了解一些关于 I2of5 条形码符号学的基本事实。I2of5 目前主要用于分销行业。I2of5 是一种纯数字符号,每个 I2of5 字符编码两个数字,一个在条形中,一个在空格中。这意味着 I2of5 消息的数字必须是偶数。每对数字都有五个条形,其中两个是宽条,三个是窄条。还有五个空格,其中两个是宽空格,三个是窄空格。10 个数字的编码模式如下所示。

字符

模式

0

nnwwn

1

wnnnw

2

nwnnw

3

wwnnn

4

nnwnw

5

wnwnn

6

nwwnn

7

nnnww

8

wnnwn

9

nwnwn

I2of5 消息以起始/停止字符开头/结尾。起始代码由两个窄条和两个窄空格组成;停止代码由一个宽条、一个窄空格和一个窄条组成。下面显示了一个示例 I2of5 消息“012345”,带有完整的起始和停止代码。

条形码位图工作区

在 Barcode Bitmap 工作区中有三个不同的项目。第一个也是最重要的项目是 bblib 项目。该项目是一个静态库,其中包含绘制所有不同类型条形码的代码。这也是本文系列文章中讨论的主要代码部分。Barcode Bitmap 工作区的另一个项目是 bbdll 项目。该项目仅仅是 bblib 静态库的一个常规 DLL 包装器。Barcode Bitmap 工作区的最后一个项目是 DLL 客户端项目。该项目是一个简单的基于对话框的应用程序,它调用 bbdll DLL 来在对话框中绘制条形码,或者将条形码作为 Windows 位图放在剪贴板上。

基类 CBarcode

本系列文章中讨论的所有条形码类型的基类是 CBarcode 类。类声明如下所示。

class CBarcode
{
    public:
        CBarcode();
        void LoadData(CString csMessage, double dNarrowBar, double dFinalHeight, 
                      HDC pDC, int nStartingXPixel, int nStartingYPixel, 
                      double dRatio = 1.0);
        virtual void DrawBitmap() = 0;
        virtual void BitmapToClipboard() = 0;
        virtual ~CBarcode();
        long GetBarcodePixelWidth();
        long GetBarcodePixelHeight();
    protected:
        CString m_csMessage;
        HDC m_hDC;
        long m_nFinalBarcodePixelWidth;
        long m_nNarrowBarPixelWidth;
        long m_nPixelHeight;
        long m_nStartingXPixel;
        long m_nStartingYPixel;
        long m_nSymbology;
        long m_nWideBarPixelWidth;
        virtual void DrawPattern(CString csPattern) = 0;
};

关于 CBarcode 类有一些需要注意的地方。首先要注意,它具有包含绘制条形码消息所需的所有有用数据的成员变量。这些数据包括窄元素像素宽度、宽元素像素宽度、消息和符号。其次,该类具有包含有关如何输出条形码消息信息的成员变量。这些数据包括设备上下文句柄以及起始 X 和 Y 像素。第三,该类有一些公共成员函数,用于通过加载数据来初始化类,并获取有关条形码消息的信息,即其像素高度和宽度。第四,该类有几个抽象成员函数,使该类成为一个抽象基类。任何派生自 CBarcode 的类都应实现这些函数。

CI2of5 类

CI2of5 类是用于绘制 I2of5 条形码的类。类的声明如下所示。

class CI2of5 : public CBarcode  
{
public:
    void BitmapToClipboard();
    void DrawBitmap();
    CI2of5();
    virtual ~CI2of5();
private:
    CString RetrievePattern(int nTwoDigitNumber);
    void DrawPattern(CString csCharPattern);

};

该类有两个公共函数 BitmapToClipboard()DrawBitmap(),另外它还从 CBarcode 类继承了 LoadData() 函数。使用该类的步骤很简单:声明该类的一个实例,调用 LoadData() 来初始化类数据,然后调用 BitmapToClipboard()(如果您想将条形码的位图放在剪贴板上),或者调用 DrawBitmap() 来绘制条形码消息。

将条形码绘制到设备上下文

以下代码片段是使用 DrawBitmap(). 的示例。

CString            csMessage;
double            dNarrowBar,dHeight, dRatio;
HDC            pDC;
long            nStartingXPixel, nStartingYPixel;
CI2of5            oBarcode;

// assign variable values here

// call LoadData and draw the barcode
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,pDC,
                  nStartingXPixel,nStartingYPixel,dRatio);
oBarcode.DrawBitmap();

将条形码绘制到剪贴板

以下代码片段是使用 BitmapToClipboard() 的示例。

HDC            hDC = NULL;
double            dNarrowbar,dHeight,dRatio;
CI2of5            oBarcode;

// assign variable values here

// call LoadData and BitmapToClipboard()
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,hDC,0,0,dRatio);
oBarcode.BitmapToClipboard();

请注意,在使用 BitmapToClipboard() 函数时,您可以在 LoadData() 调用中传递一个空设备上下文句柄以及零作为起始 X 和 Y 像素。显然,起始 X 和 Y 像素在剪贴板上没有意义,但空设备上下文句柄呢?问题的答案可以通过查看 BitmapToClipboard() 函数中的这段代码片段找到。

CDC    memDC;
memDC.CreateCompatibleDC(NULL);

因此,BitmapToClipboard() 函数通过调用 memDC.CreateCompatibleDC(NULL) 函数来创建自己的内存设备上下文。快速查看 MSDN 文档会发现,如果将 NULL 值传递给 CreateCompatibleDC,创建的设备上下文将与屏幕兼容。

CBarcode::LoadData() 详细信息

CBarcode::LoadData() 的参数值得进一步解释,这里似乎是合适的。第一个参数 csMessage 只是您希望绘制为 I2of5 条形码的消息。下一个参数 dNarrowBar 是窄元素的宽度(以英寸为单位)。参数 dHeight 是条形码的高度(以英寸为单位)。参数 pDC 是要在其中绘制条形码的设备上下文的句柄。接下来的两个参数 nStartingXPixelnStartingYPixel 定义了绘制条形码的起始坐标。最后一个参数 dRatio 是宽/窄元素宽度的比率。如果您还记得上面 CBarcode 类的声明,您会记得它以像素为单位存储所有宽度和高度信息,并且它存储窄元素宽度和宽元素宽度,而不是窄元素宽度和宽/窄元素宽度比。显然 CBarcode::LoadData() 在幕后进行了一些转换工作。

该转换工作的第一个步骤是获取 X 轴和 Y 轴 DPI,这通过以下代码完成,取自 CBarcode::LoadData()

CDC    tempDC;
tempDC.Attach(m_hDC);
nXAxisDpi = tempDC.GetDeviceCaps(LOGPIXELSX);
nYAxisDpi = tempDC.GetDeviceCaps(LOGPIXELSY);
tempDC.Detach();

一旦有了 X 和 Y 轴 DPI,就可以计算像素高度、窄元素像素宽度和宽元素像素宽度,如下面的代码片段所示。

// load the final attributes that depend on the device context
m_nPixelHeight = (int)((nYAxisDpi*dFinalHeight)+0.5);
m_nNarrowBarPixelWidth = (int)((nXAxisDpi*dNarrowBar)+0.5);
m_nWideBarPixelWidth = (int)(dRatio*m_nNarrowBarPixelWidth);

注意计算窄元素像素宽度和宽元素像素宽度时的四舍五入效应。窄元素的宽度有一个像素的下限,因此您可以生成的条形码受到输出设备的物理限制。

接下来,您可以计算最终的条形码像素宽度,此操作特定于符号,下面列出了 Code 39 的代码摘录。

// add start code
m_nFinalBarcodePixelWidth = 4 * m_nNarrowBarPixelWidth;

// add message
m_nFinalBarcodePixelWidth += ((3*m_nNarrowBarPixelWidth)+(2*m_nWideBarPixelWidth))
                              *m_csMessage.GetLength();
        
// add stop code
m_nFinalBarcodePixelWidth += (2*m_nNarrowBarPixelWidth)+(m_nWideBarPixelWidth);

这段代码将起始代码、消息和停止代码的宽度相加,以确定最终的条形码宽度。

CI2of5::DrawBitmap() 详细信息

DrawBitmap() 函数是绘制每个消息字符的地方。下面列出了 CI2of5::DrawBitmap() 函数的列表。

void CI2of5::DrawBitmap()
{
    int        i,nNumber;

    // draw the start character
    DrawPattern("nnnn");

    // for each character in the message
    for (i=0;i<m_csMessage.GetLength();i+=2)
    {
        // retrieve the next two digit number
        nNumber = m_csMessage.GetAt(i) - '0';
        nNumber = nNumber * 10;
        nNumber += m_csMessage.GetAt(i+1) - '0';

        // draw the two digit number
        DrawPattern(RetrievePattern(nNumber));
    }

    // draw the stop character
    DrawPattern("wnn");

    return;
}

CI2of5::DrawBitmap() 函数首先绘制起始代码。然后代码遍历消息中的每个字符,并将字符以两位数对的形式绘制。这里使用了两个私有成员函数。CI2of5::DrawPattern() 绘制传递给它的模式,模式是 CString,形式为“nnnnwwwwnn”(两位数对 '00'),就像上面提到的字符数据一样。CI2of5::RetrievePattern()basically 是一个大型 switch 语句,检索传递给它的任何合法两位数对(00 到 99)的模式。(请注意,从 CI2of5::RetrievePattern() 返回的每个字符模式都绘制了两位数对,条形是左边的数字,空格是右边的数字。)最后,代码绘制停止字符,条形码消息就完成了。

CI2of5::DrawPattern() 详细信息

CI2of5::DrawPattern() 函数在传递的设备上下文中绘制单个 I2of5 条形码字符。下面列出了 CI2of5::DrawPattern() 函数。

void CI2of5::DrawPattern(CString csCharPattern)
{
    int            i,nXPixel,nYPixel,nTempWidth;
    CDC            oDC;

    // attach to the device context
    oDC.Attach(m_hDC);

    // initialize X pixel value
    nXPixel = m_nStartingXPixel;
    
    for (i=0;i<csCharPattern.GetLength();i++)
    {
        // decide if narrow or wide bar
        if (csCharPattern.GetAt(i)=='n')
            nTempWidth = m_nNarrowBarPixelWidth;
        else
            nTempWidth = m_nWideBarPixelWidth;
        
        // X value for loop
        for (nXPixel=m_nStartingXPixel;
             nXPixel<m_nStartingXPixel+nTempWidth;
             nXPixel++)
        {
            // Y value for loop
            for (nYPixel=m_nStartingYPixel;
                 nYPixel<m_nStartingYPixel+m_nPixelHeight;
                 nYPixel++)
            {
                // if this is a bar
                if (i%2==0)
                    oDC.SetPixelV(nXPixel,nYPixel,COLORBLACK);
                else
                    oDC.SetPixelV(nXPixel,nYPixel,COLORWHITE);
            }
        }

        // advance the starting position
        m_nStartingXPixel+= nTempWidth;
    }

    // detach from the device context
    oDC.Detach();
    
    return;
}

CI2of5::DrawPattern() 函数基本上是三个循环。最外层循环遍历模式(nnnnwwwwnn)中的每个两位数对。中间循环遍历当前窄或宽元素宽度中的每个 X 像素。最内层循环遍历当前 X 像素中的每个 Y 像素。在三个循环的中心是一个简单的 if 语句,它确定我们是正在绘制条形还是空格,并将当前像素设置为黑色或白色,分别代表条形或空格。该函数将为起始字符、所有消息字符和停止字符重复执行,以绘制完整的 I2of5 条形码。

摘要

绘制 I2of5 条形码的内容到此结束。本系列的第四部分将介绍绘制 Code 93 条形码。希望您觉得这个类库很有用。

参考

条形码书籍 - 读取、打印、指定和应用条形码及其他机器可读符号的综合指南 第 4 版

作者:Roger C. Palmer

版权所有 1989, 1991, 1995, 2001 Helmers Publishing, Inc.

ISBN 0-911261-13-3

© . All rights reserved.