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






4.44/5 (9投票s)
一篇关于将 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
是要在其中绘制条形码的设备上下文的句柄。接下来的两个参数 nStartingXPixel
和 nStartingYPixel
定义了绘制条形码的起始坐标。最后一个参数 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