Introduction
A recent project at work required that I write out barcode
characters into a font file for an old photo-typesetter. This
experience inspired me to start a side project writing some code that
renders a barcode on the Windows screen, given the proper input. This
series of articles is the result of that project.
Code 39 basics
This first article is about drawing Code 39 barcodes on the
Windows screen. Before I start discussing the code, we'll need to
know some basic facts about the Code 39 barcode symbology. Code 39
was the first alphanumeric symbology developed, and is widely used in
industrial settings. Code 39 has two different element widths, wide
and narrow, which are usually specified by giving the narrow width
and the narrow/wide ratio. Each Code 39 character has five bars and
four spaces for a total of nine elements. Of the nine elements, three
are wide and six are narrow, leading to the name Code 39 (3 of 9).
Each character is followed by an inter-character gap, usually equal
to the width of a narrow element. The 44 characters in the Code 39
symbology are listed below:
Character
|
Pattern (bsbsbsbsb)
|
1
|
wnnwnnnnw
|
2
|
nnwwnnnnw
|
3
|
wnwwnnnnn
|
4
|
nnnwwnnnw
|
5
|
wnnwwnnnn
|
6
|
nnwwwnnnn
|
7
|
nnnwnnwnw
|
8
|
wnnwnnwnn
|
9
|
nnwwnnwnn
|
0
|
nnnwwnwnn
|
A
|
wnnnnwnnw
|
B
|
nnwnnwnnw
|
C
|
wnwnnwnnn
|
D
|
nnnnwwnnw
|
E
|
wnnnwwnnn
|
F
|
nnwnwwnnn
|
G
|
nnnnnwwnw
|
H
|
wnnnnwwnn
|
I
|
nnwnnwwnn
|
J
|
nnnnwwwnn
|
K
|
wnnnnnnww
|
L
|
nnwnnnnww
|
M
|
wnwnnnnwn
|
N
|
nnnnwnnww
|
O
|
wnnnwnnwn
|
P
|
nnwnwnnwn
|
Q
|
nnnnnnwww
|
R
|
wnnnnnwwn
|
S
|
nnwnnnwwn
|
T
|
nnnnwnwwn
|
U
|
wwnnnnnnw
|
V
|
nwwnnnnnw
|
W
|
wwwnnnnnn
|
X
|
nwnnwnnnw
|
Y
|
wwnnwnnnn
|
Z
|
nwwnwnnnn
|
-
|
nwnnnnwnw
|
.
|
wwnnnnwnn
|
SPACE
|
nwwnnnwnn
|
*
|
nwnnwnwnn
|
$
|
nwnwnwnnn
|
/
|
nwnwnnnwn
|
+
|
nwnnnwnwn
|
%
|
nnnwnwnwn
|
A Code 39 message begins and ends with an asterisk, which serves
as this symbologies start/stop code. A sample Code 39 message “DATA”
is pictured below complete with start and stop codes.
The Barcode Bitmap Workspace
There are three different projects in the Barcode Bitmap
workspace. The first and most important project is the bblib project.
This project is a static library where code to draw all of the
different types of barcodes exists. This also is the main piece of
code discussed in this series of articles. Another project Barcode
Bitmap workspace is the bbdll project. This project is simply a
regular DLL wrapper around the bblib static library. The final
project in the Barcode Bitmap workspace is the DLL client project.
This project is a simple dialog-based application that calls the
bbdll DLL to draw barcodes in the dialog, or put barcodes on the
clipboard as Windows bitmaps.
The base class CBarcode
The base class for all the barcode types discussed in this series
of articles is the CBarcode
class. The class declaration is listed
below.
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;
};
There are a few things to note about the CBarcode
class. First
note that it has data members that contain all of the useful data
needed to draw a barcode message. This data includes the narrow
element pixel width, the wide element pixel width, the message,
and the symbology. Second the class has data members that contain
information about how to output the barcode message. This data
includes a device context handle, and a starting X and Y pixel.
Third the class has some public member functions to intialize the
class by loading data, and obtain information about the barcode
message, namely its pixel height and width. Fourth the class has
several abstract member functions that make this class an abstract
base class. Any classes derived from CBarcode
will be expected to
implement these functions.
The CCode39 class
The CCode39
class is the class to implement to draw a Code 39
barcode. The class declaration is listed below.
class CCode39 : public CBarcode
{
public:
void BitmapToClipboard();
void DrawBitmap();
CCode39();
virtual ~CCode39();
private:
void DrawPattern(CString csPattern);
CString RetrievePattern( char c );
};
The class has two public functions BitmapToClipboard()
and
DrawBitmap()
, plus it inherits the LoadData()
function from the
CBarcode
class. The steps to use the class are simple, declare an
instance of the class, call LoadData()
to intialize class data, and
then call either BitmapToClipboard()
if you want to put a bitmap of
the barcode on the clipboard, or call DrawBitmap()
to draw the
barcode message.
Drawing a Barcode to a Device Context
The following code snipet is an example using DrawBitmap()
.
CString csMessage;
double dNarrowBar,dHeight, dRatio;
HDC pDC;
long nStartingXPixel, nStartingYPixel;
CCode39 oBarcode;
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,pDC,nStartingXPixel,
nStartingYPixel,dRatio);
oBarcode.DrawBitmap();
Drawing a Barcode to the Clipboard
The following code snipet is an example using BitmapToClipboard()
.
HDC hDC = NULL;
double dNarrowbar,dHeight,dRatio;
CCode93 oBarcode;
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,hDC,0,0,dRatio);
oBarcode.BitmapToClipboard();
Note that when using the BitmapToClipboard()
function, you can
pass a null device context handle and zeroes for the starting X and Y
pixel in the LoadData() call. Obviously the starting X and Y pixels
are meaningless on the clipboard, but what about the null device
context handle? The answer to that question can be found by looking
at this code snipet from the BitmapToClipboard()
function.
CDC memDC;
memDC.CreateCompatibleDC(NULL);
So the BitmapToClipboard()
function creates its own memory device
context by using the memDC.CreateCompatibleDC(NULL)
function call. A
quick look at the MSDN documentation shows that if you pass a NULL
value to CreateCompatibleDC
, the device context created is compatible
with the screen.
CBarcode::LoadData() details
The parameters for CBarcode::LoadData()
deserve some further
explanation and this seems like the place to do it. The first
parameter, csMessage
is simply the message you wish to be drawn as a
Code 39 barcode. The next parameter dNarrowBar
is the width of the
narrow element in inches. The parameter dHeight
is the height of the
barcode in inches. The parameter pDC
is a handle to the device
context that the barcode will be drawn in. The next two parameters,
nStartingXPixel
and nStartingYPixel
define the coordinates to start
drawing the barcode. The final parameter, dRatio
is the ratio of
wide/narrow element widths. If you remember the declaration of the
CBarcode
class above, you'll remember that it stores all width and
height information in pixels, and that it stores the narrow element
width and the wide element width instead of the narrow element width
and the wide/narrow element width ratio. Clearly CBarcode::LoadData()
is doing some behind the scenes conversion work.
The first step to that conversion work is to get the X axis and Y
axis dpi, which is done by the following code, taken from
CBarcode::LoadData()
.
CDC tempDC;
tempDC.Attach(m_hDC);
nXAxisDpi = tempDC.GetDeviceCaps(LOGPIXELSX);
nYAxisDpi = tempDC.GetDeviceCaps(LOGPIXELSY);
tempDC.Detach();
Once you have the X and Y axis dpi, you can calculate the pixel
height, narrow element pixel width, and wide element pixel width as
shown in the following code snipet.
m_nPixelHeight = (int)((nYAxisDpi*dFinalHeight)+0.5);
m_nNarrowBarPixelWidth = (int)((nXAxisDpi*dNarrowBar)+0.5);
m_nWideBarPixelWidth = (int)(dRatio*m_nNarrowBarPixelWidth);
Note the rounding effect when calculating the narrow element pixel
width and the wide element pixel width. The narrow element width has
a lower limit of one pixel, so the barcode you can produce is limited
by the physical limitations of the output device.
Next you can calculate the final barcode pixel width, this
operation is symbology specific and the Code 39 code excerpt is
listed below.
nTemp = m_csMessage.GetLength() + 2;
m_nFinalBarcodePixelWidth = nTemp * ((3*m_nWideBarPixelWidth) +
(7*m_nNarrowBarPixelWidth));
The code above calculates the final barcode pixel width by adding two characters for the
message length, (for the start and stop code) and multiplying the total number of characters
by the pixel length of each character. Note that the total length of each character uses 7
times the narrow element width to accomodate the inter-character gap.
CCode39::DrawBitmap() details
The DrawBitmap()
function is where each message character is
drawn. A listing of the CCode39::DrawBitmap()
function is listed
below.
void CCode39::DrawBitmap()
{
int i;
CString csCurrentPattern;
DrawPattern(RetrievePattern('*'));
for (i=0;i<m_csMessage.GetLength();i++)
DrawPattern(RetrievePattern(m_csMessage.GetAt(i)));
DrawPattern(RetrievePattern('*'));
return;
}
The CCode39::DrawBitmap()
function starts out by drawing the start
character, the asterisk. Then the code steps through every character
in the message and draws each character. Finally the code draws the
stop character, again the asterisk. There are two private member
functions that are used here. CCode39::DrawPattern()
draws the
pattern passed to it, the pattern is a CString
in the form of
“wnnwnnnnw” (the character '1') like the character data
mentioned above. CCode39::RetrievePattern()
is basically a giant
switch statement, retrieving the pattern for any legal Code 39
character passed to it. Note that each character pattern returned
from CCode39::RetrievePattern()
has an extra “n” tacked
on to the end to add the inter-character gap.
CCode39::DrawPattern() details
The CCode39::DrawPattern()
function draws a single Code 39 barcode
character in the passed device context. The CCode39::DrawPattern()
function is listed below.
void CCode39::DrawPattern( CString csPattern )
{
int i,nXPixel,nYPixel,nTempWidth;
CDC oDC;
oDC.Attach(m_hDC);
nXPixel = m_nStartingXPixel;
for (i=0;i<csPattern.GetLength();i++)
{
if (csPattern.GetAt(i)=='n')
nTempWidth = m_nNarrowBarPixelWidth;
else
nTempWidth = m_nWideBarPixelWidth;
for (nXPixel=m_nStartingXPixel;
nXPixel<m_nStartingXPixel+nTempWidth;
nXPixel++)
{
for (nYPixel = m_nStartingYPixel;
nYPixel<m_nStartingYPixel+m_nPixelHeight;
nYPixel++)
{
if (i%2==0)
oDC.SetPixelV(nXPixel,nYPixel,COLORBLACK);
else
oDC.SetPixelV(nXPixel,nYPixel,COLORWHITE);
}
}
m_nStartingXPixel+= nTempWidth;
}
oDC.Detach();
return;
}
The CCode39::DrawPattern()
function is basically three loops. The
outermost loop loops thru every character in the pattern (nnwnnnnww).
The middle loop loops through every X pixel in the current narrow or
wide element width. The innermost loop loops through every Y pixel in
the current X pixel. In the center of the three loops is a simple if
statement that determines if we're drawing a bar or a space, and sets
the current pixel to black or white for a bar or space. This function
is repeated for the start character, all the message characters, and
the stop character to draw the complete Code 39 barcode.
Summary
That's it for drawing Code 39 barcodes. Part II of the series
deals with drawing Codabar barcodes. I hope you find this class
library useful.
Reference
The Bar Code Book - A Comprehensive Guide To Reading, Printing, Specifying,
and Applying Bar Code and Other Machine-Readable Symbols 4th Edition
By Roger C. Palmer
Copyright 1989,1991, 1995, 2001 by Helmers Publishing, Inc.
ISBN 0-911261-13-3