在 Windows 中绘制条形码第二部分 - Codebar






4.83/5 (12投票s)
一篇关于将 Codebar 条形码绘制到屏幕或剪贴板的文章。
引言
公司最近的一个项目要求我将条形码字符写入一个旧式照相排版机的字体文件中。这段经历启发我开始了一个副项目,编写一些代码,在给定正确输入的情况下,在 Windows 屏幕上渲染条形码。这一系列文章就是这个项目的成果。
Codabar 基础知识
第二篇文章是关于在 Windows 屏幕上绘制 Codabar 条形码。在开始讨论代码之前,我们需要了解一些关于 Codabar 条形码符号学的基本事实。Codabar 目前用于血库、图书馆和某些快递航空包裹应用。Codabar 具有两种不同的元素宽度,宽和窄,通常通过给出窄宽度和窄/宽比来指定。每个 Codabar 字符由四个条形和三个间隔组成,总共七个元素。由于符号学的结构,有两种不同的字符宽度。每个字符后面都有一个字符间间隔,通常是窄元素的宽度。Codabar 符号学的 20 个字符如下所示:
字符 | 模式 (bsbsbsb) |
0 | nnnnnww |
1 | nnnnwwn |
2 | nnnwnnw |
3 | wwnnnnn |
4 | nnwnnwn |
5 | wnnnnwn |
6 | nwnnnnw |
7 | nwnnwnn |
8 | nwwnnnn |
9 | wnnwnnn |
- | nnnwwnn |
$ | nnwwnnn |
: | wnnnwnw |
/ | wnwnnnw |
. | wnwnwnn |
+ | nnwnwnw |
A | nnwwnwn |
B | nwnwnnw |
C | nnnwnww |
D | nnnwwwn |
Codabar 消息以起始/停止字符 (A-D) 开始/结束。通过选择起始/停止字符,可以编码其他数据。下面的示例 Codabar 消息“012345”显示了完整的起始和停止代码。
条形码位图工作区
条形码位图工作区中有三个不同的项目。第一个也是最重要的项目是 bblib 项目。这是一个静态库,其中包含绘制所有不同类型条形码的代码。这也是本文系列讨论的主要代码。另一个项目 Barcode Bitmap workspace 是 bbdll 项目。这个项目只是围绕 bblib 静态库的一个普通 DLL 包装器。Barcode Bitmap workspace 中的最后一个项目是 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
的类都将需要实现这些函数。
CRationalCodabar 类
CRationalCodabar
类是用于绘制 Codabar 条形码的实现类。类声明如下所示:
class CRationalCodabar : public CBarcode { public: void DrawBitmap(); void BitmapToClipboard(); CRationalCodabar(); virtual ~CRationalCodabar(); private: CString RetrievePattern(char c); void DrawPattern(CString csPattern); };
该类有两个公共函数 BitmapToClipboard()
和 DrawBitmap()
,此外它还从 CBarcode
类继承了 LoadData()
函数。使用该类的步骤很简单:声明类实例,调用 LoadData()
初始化类数据,然后调用 BitmapToClipboard()
(如果您想将条形码的位图放入剪贴板),或者调用 DrawBitmap()
绘制条形码消息。
将条形码绘制到设备上下文
以下代码片段是使用 DrawBitmap().
的示例。
CString csMessage; double dNarrowBar,dHeight, dRatio; HDC pDC; long nStartingXPixel, nStartingYPixel; CRationalCodabar 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; CRationalCodabar 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
只是您希望绘制为 Codabar 条形码的消息。下一个参数 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);
注意计算窄元素像素宽度和宽元素像素宽度时的四舍五入效应。窄元素的宽度有一个像素的下限,因此您可以生成的条形码受到输出设备的物理限制。
接下来,您可以计算最终条形码像素宽度,此操作特定于符号,Codabar 代码摘录如下所示:
// initialize to zero m_nFinalBarcodePixelWidth = 0; // add the width of each character for (i=0;i<csMessage.GetLength();i++) { c = csMessage.GetAt(i); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '$': m_nFinalBarcodePixelWidth +=(6*m_nNarrowBarPixelWidth)+ (2*m_nWideBarPixelWidth); break; case ':': case '/': case '.': case '+': case 'A': case 'B': case 'C': case 'D': m_nFinalBarcodePixelWidth +=(5*m_nNarrowBarPixelWidth)+ (3*m_nWideBarPixelWidth); break; } }
此代码循环遍历每个消息字符,为每个字符选择两种可用宽度之一,并累加总条形码宽度。
CRationalCodabar::DrawBitmap() 详细信息
DrawBitmap()
函数用于绘制每个消息字符。下面列出了 CRationalCodabar::DrawBitmap()
函数的列表。
void CRationalCodabar::DrawBitmap() { int i; // draw each character in the message for (i=0;i<m_csMessage.GetLength();i++) DrawPattern(RetrievePattern(m_csMessage.GetAt(i))); return; }
CRationalCodabar::DrawBitmap()
函数无法知道您想要哪个起始或停止代码,因此假定它们是消息的一部分。代码逐个字符遍历消息并绘制每个字符。这里使用了两个私有成员函数。CRationalCodabar ::DrawPattern()
绘制传递给它的模式,模式是一个 CString
,形式为“nnnnwwn”(字符“1”),就像上面提到的字符数据一样。CRationalCodabar::RetrievePattern()
basically 是一个巨大的 switch 语句,用于检索任何合法 Codabar 字符的模式。请注意,从 CRationalCodabar::RetrievePattern()
返回的每个字符模式的末尾都附加了一个额外的“n”以添加字符间间隔。
CRationalCodabar::DrawPattern() 详细信息
CRationalCodabar::DrawPattern()
函数在传递的设备上下文中绘制单个 Codabar 条形码字符。下面列出了 CRationalCodabar::DrawPattern()
函数。
void CRationalCodabar::DrawPattern(CString csPattern) { 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<csPattern.GetLength();i++) { // decide if narrow or wide bar if (csPattern.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; }
CRationalCodabar::DrawPattern()
函数基本上是三个循环。最外层循环遍历模式中的每个字符 (nnnnwwn)。中间循环遍历当前窄或宽元素宽度中的每个 X 像素。最内层循环遍历当前 X 像素中的每个 Y 像素。在三个循环的中心是一个简单的 if 语句,它决定是绘制条形还是空间,并将当前像素设置为黑色或白色,分别表示条形或空间。此函数会重复用于起始字符、所有消息字符和停止字符,以绘制完整的 Codabar 条形码。
摘要
以上就是 Codabar 条形码的绘制。本系列文章的第三部分将介绍 I2of5 条形码的绘制。希望您觉得这个类库很有用。
参考
条形码书籍 - 读取、打印、指定和应用条形码及其他机器可读符号的综合指南 第 4 版
作者:Roger C. Palmer
版权所有 1989, 1991, 1995, 2001 Helmers Publishing, Inc.
ISBN 0-911261-13-3