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 128 basics
Code 128 is a very high density alpha-numeric code that was introduced
in 1981 and is being used in a variety of applications. Code 128 characters
consist of 3 bars and 3 spaces, which are built by using 11 modules each of
which can be black or white. Because all of the characters are built from
the same 11 modules, there really is only one element width that needs to
be defined, the module width. The bar/space patterns for the 107 data
characters and the start/stop codes are listed below. Each pattern is 11
characters (modules) long, and is either 'b' (that module is a bar) or 's'
(that module is a space). Note that there are three subsets to Code 128,
Subset A and B cover the ASCII character set, and Subset C is a double-density
numeric-only subset. Three different start characters tell the barcode
reader which subset is the starting subset, and three shift characters allow
changing subsets within a Code 128 barcode.
Code A
|
Code B
|
Code C
|
Value
|
Pattern
|
Space
|
Space
|
00
|
0
|
bbsbbssbbss
|
!
|
!
|
01
|
1
|
bbssbbsbbss
|
“
|
“
|
02
|
2
|
bbssbbssbbs
|
#
|
#
|
03
|
3
|
bssbssbbsss
|
$
|
$
|
04
|
4
|
bssbsssbbss
|
%
|
%
|
05
|
5
|
bsssbssbbss
|
&
|
&
|
06
|
6
|
bssbbssbsss
|
'
|
(
|
07
|
7
|
bssbbsssbss
|
(
|
)
|
08
|
8
|
bsssbbssbss
|
)
|
*
|
09
|
9
|
bbssbssbsss
|
*
|
*
|
10
|
10
|
bbssbsssbss
|
+
|
+
|
11
|
11
|
bbsssbssbss
|
,
|
,
|
12
|
12
|
bsbbssbbbss
|
-
|
-
|
13
|
13
|
bssbbsbbbss
|
.
|
.
|
14
|
14
|
bssbbssbbbs
|
/
|
/
|
15
|
15
|
bsbbbssbbss
|
0
|
0
|
16
|
16
|
bssbbbsbbss
|
1
|
1
|
17
|
17
|
bssbbbssbbs
|
2
|
2
|
18
|
18
|
bbssbbbssbs
|
3
|
3
|
19
|
19
|
bbssbsbbbss
|
4
|
4
|
20
|
20
|
bbssbssbbbs
|
5
|
5
|
21
|
21
|
bbsbbbssbss
|
6
|
6
|
22
|
22
|
bbssbbbsbss
|
7
|
7
|
23
|
23
|
bbbsbbsbbbs
|
8
|
8
|
24
|
24
|
bbbsbssbbss
|
9
|
9
|
25
|
25
|
bbbssbsbbss
|
:
|
:
|
26
|
26
|
bbbssbssbbs
|
;
|
;
|
27
|
27
|
bbbsbbssbss
|
<
|
<
|
28
|
28
|
bbbssbbsbss
|
Equal
|
Equal
|
29
|
29
|
bbbssbbssbs
|
>
|
>
|
30
|
30
|
bbsbbsbbsss
|
?
|
?
|
31
|
31
|
bbsbbsssbbs
|
@
|
@
|
32
|
32
|
bbsssbbsbbs
|
A
|
A
|
33
|
33
|
bsbsssbbsss
|
B
|
B
|
34
|
34
|
bsssbsbbsss
|
C
|
C
|
35
|
35
|
bsssbsssbbs
|
D
|
D
|
36
|
36
|
bsbbsssbsss
|
E
|
E
|
37
|
37
|
bsssbbsbsss
|
F
|
F
|
38
|
38
|
bsssbbsssbs
|
G
|
G
|
39
|
39
|
bbsbsssbsss
|
H
|
H
|
40
|
40
|
bbsssbsbsss
|
I
|
I
|
41
|
41
|
bbsssbsssbs
|
J
|
J
|
42
|
42
|
bsbbsbbbsss
|
K
|
K
|
43
|
43
|
bsbbsssbbbs
|
L
|
L
|
44
|
44
|
bsssbbsbbbs
|
M
|
M
|
45
|
45
|
bsbbbsbbsss
|
N
|
N
|
46
|
46
|
bsbbbsssbbs
|
O
|
O
|
47
|
47
|
bsssbbbsbbs
|
P
|
P
|
48
|
48
|
bbbsbbbsbbs
|
Q
|
Q
|
49
|
49
|
bbsbsssbbbs
|
R
|
R
|
50
|
50
|
bbsssbsbbbs
|
S
|
S
|
51
|
51
|
bbsbbbsbsss
|
T
|
T
|
52
|
52
|
bbsbbbsssbs
|
U
|
U
|
53
|
53
|
bbsbbbsbbbs
|
V
|
V
|
54
|
54
|
bbbsbsbbsss
|
W
|
W
|
55
|
55
|
bbbsbsssbbs
|
X
|
X
|
56
|
56
|
bbbsssbsbbs
|
Y
|
Y
|
57
|
57
|
bbbsbbsbsss
|
Z
|
Z
|
58
|
58
|
bbbsbbsssbs
|
[
|
[
|
59
|
59
|
bbbsssbbsbs
|
\
|
\
|
60
|
60
|
bbbsbbbbsbs
|
]
|
]
|
61
|
61
|
bbssbssssbs
|
^
|
^
|
62
|
62
|
bbbbsssbsbs
|
_
|
_
|
63
|
63
|
bsbssbbssss
|
NUL
|
`
|
64
|
64
|
bsbssssbbss
|
SOH
|
a
|
65
|
65
|
bssbsbbssss
|
STX
|
b
|
66
|
66
|
bssbssssbbs
|
ETX
|
c
|
67
|
67
|
bssssbsbbss
|
EOT
|
d
|
68
|
68
|
bssssbssbbs
|
ENQ
|
e
|
69
|
69
|
bsbbssbssss
|
ACK
|
f
|
70
|
70
|
bsbbssssbss
|
BEL
|
g
|
71
|
71
|
bssbbsbssss
|
BS
|
h
|
72
|
72
|
bssbbssssbs
|
HT
|
i
|
73
|
73
|
bssssbbsbss
|
LF
|
j
|
74
|
74
|
bssssbbssbs
|
VT
|
k
|
75
|
75
|
bbssssbssbs
|
FF
|
l
|
76
|
76
|
bbssbsbssss
|
CR
|
m
|
77
|
77
|
bbbbsbbbsbs
|
SO
|
n
|
78
|
78
|
bbssssbsbss
|
SI
|
o
|
79
|
79
|
bsssbbbbsbs
|
DLE
|
p
|
80
|
80
|
bsbssbbbbss
|
DC1
|
q
|
81
|
81
|
bssbsbbbbss
|
DC2
|
r
|
82
|
82
|
bssbssbbbbs
|
DC3
|
s
|
83
|
83
|
bsbbbbssbss
|
DC4
|
t
|
84
|
84
|
bssbbbbsbss
|
NAK
|
u
|
85
|
85
|
bssbbbbssbs
|
SYN
|
v
|
86
|
86
|
bbbbsbssbss
|
ETB
|
w
|
87
|
87
|
bbbbssbsbss
|
CAN
|
x
|
88
|
88
|
bbbbssbssbs
|
EM
|
y
|
89
|
89
|
bbsbbsbbbbs
|
SUB
|
z
|
90
|
90
|
bbsbbbbsbbs
|
ESC
|
{
|
91
|
91
|
bbbbsbbsbbs
|
FS
|
|
|
92
|
92
|
bsbsbbbbsss
|
GS
|
}
|
93
|
93
|
bsbsssbbbbs
|
RS
|
~
|
94
|
94
|
bsssbsbbbbs
|
US
|
DEL
|
95
|
95
|
bsbbbbsbsss
|
FNC3
|
FNC3
|
96
|
96
|
bsbbbbsssbs
|
FNC2
|
FNC2
|
97
|
97
|
bbbbsbsbsss
|
Shift
|
Shift
|
98
|
98
|
bbbbsbsssbs
|
Switch Code C
|
Switch Code C
|
99
|
99
|
bsbbbsbbbbs
|
Switch Code B
|
FNC4
|
Switch Code B
|
100
|
bsbbbbsbbbs
|
FNC4
|
Switch Code A
|
Switch Code A
|
101
|
bbbsbsbbbbs
|
FNC1
|
FNC1
|
FNC1
|
102
|
bbbbsbsbbbs
|
START Code A
|
START Code A
|
START Code A
|
103
|
bbsbsbbbbss
|
START Code B
|
START Code B
|
START Code B
|
104
|
bbsbssbssss
|
START Code C
|
START Code C
|
START Code C
|
105
|
bbsbssbbbss
|
STOP
|
STOP
|
STOP
|
106
|
bbsssbbbsbsbb
|
Each Code 128 barcode has a check digit that immediately precedes
the stop character. The check digit is a weighted sum of the data
characters, modulus 103. The data characters are weight from left to
right by the infinite sequence {1,2,3,...} An example of the check
digit calculations is shown below, using the message "DATA".
Start A
|
D
|
A
|
T
|
A
|
Check Digit
|
Stop
|
|
36
|
33
|
52
|
33
|
|
|
|
1
|
2
|
3
|
4
|
|
|
To calculate the check digit, first find the sum of products: (36*1)+(33*2)+(52*3)+(33*4) =
390.
Divide 390 by 103 to get 3 with a remainder of 81. The value of the check digit is 81 which corresponds
to the ASCII character DC1. A picture of the entire barcode is shown below.
Note that the barcode reader uses the check digits to decode the barcode, but does
not transmit them.
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 CCode128 class
The CCode128 class is the class to implement to draw a Code 128
barcode. The class declaration is listed below.
class CCode128 : public CBarcode
{
public:
CCode128();
virtual ~CCode128();
public:
void BitmapToClipboard();
void DrawBitmap();
void LoadData(CString csMessage, double dNarrowBar,
double dFinalHeight, HDC pDC, int nStartingXPixel,
int nStartingYPixel, long nStartingSubset );
private:
long GetCheckDigit();
void DrawPattern(CString csPattern);
CString RetrievePattern(long c);
long m_nCurrentSubset;
};
The class has three public functions BitmapToClipboard() and DrawBitmap(), and
LoadData(). The class also has a
data member, m_nCurrentSubset, which holds the current subset while drawing the
barcode, and is initialized to the starting subset. The steps to use the
class are simple, declare an instance of the class, call LoadData()
to initialize 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;
HDC pDC;
long nStartingXPixel, nStartingYPixel, nCode128StartingSubset;
CCode128 oBarcode;
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,pDC,nStartingXPixel,
nStartingYPixel,nCode128StartingSubset);
oBarcode.DrawBitmap();
Drawing a Barcode to the Clipboard
The following code snipet is an example using BitmapToClipboard().
HDC hDC = NULL;
double dNarrowbar,dHeight;
CCode128 oBarcode;
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,hDC,0,0,nCode128StartingSubset);
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.
CCode128::LoadData() details
The code for CCode128::LoadData() is listed below.
void CCode128::LoadData(CString csMessage, double dNarrowBar,
double dFinalHeight, HDC pDC,
int nStartingXPixel, int nStartingYPixel,
long nStartingSubset )
{
CBarcode::LoadData(csMessage, dNarrowBar, dFinalHeight, pDC,
nStartingXPixel, nStartingYPixel);
m_nCurrentSubset = nStartingSubset;
}
As you can see, the CCode128::LoadData() gets most of its functionality from the base
class function CBarcode::LoadData() which is discussed below. The Code 128 specific
attribute that made this function necessary is m_nCurrentSubset, which is initialized to the starting
subset of the Code 128 barcode you are drawing.
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 128 barcode. The next parameter dNarrowBar is the width
of each module 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, and has no use in a Code 128 barcode. If you look at the
declaration of CBarcode::LoadData(), you'll see the parameter
dRatio has a default value of 1.0. So when using LoadData()
for a Code 128 barcode, you can just leave out the dRatio parameter.
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. Also note that for
Code 128, the ratio will always be 1.0 and the member variable m_nWideBarPixelWidth will always equal
m_nNarrowBarPixelWidth
and will not be used.
Next you can calculate the final barcode pixel width, this
operation is symbology specific and the Code 128 code excerpt is
listed below.
nTemp = m_csMessage.GetLength();
m_nFinalBarcodePixelWidth = ((nTemp*11)+35)*m_nNarrowBarPixelWidth;
This code computes the width of a Code 128 barcode by taking the message
length, multiplying it by 11 (11 modules per character), adding 35 (start character,
check digit, and stop character plus 2 module termination bar), and multiplying that
total by the module width to get the total barcode pixel width.
CCode128::DrawBitmap() details
The DrawBitmap() function is where each message character is
drawn.. A listing of the CCode128::DrawBitmap() function is
listed below.
void CCode128::DrawBitmap()
{
long nChar,nNextChar,nCharacterPosition,nCheckDigit;
nCheckDigit = GetCheckDigit();
if (m_nCurrentSubset==SUBSETA)
DrawPattern(RetrievePattern(103));
else if (m_nCurrentSubset==SUBSETB)
DrawPattern(RetrievePattern(104));
else if (m_nCurrentSubset==SUBSETC)
DrawPattern(RetrievePattern(105));
nCharacterPosition = 0;
while (nCharacterPosition < m_csMessage.GetLength())
{
if (m_nCurrentSubset==SUBSETC)
{
if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==101)
{
DrawPattern(RetrievePattern(101));
nCharacterPosition++;
m_nCurrentSubset = SUBSETA;
}
else if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==100)
{
DrawPattern(RetrievePattern(100));
nCharacterPosition++;
m_nCurrentSubset = SUBSETB;
}
else if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==102)
{
DrawPattern(RetrievePattern(100));
nCharacterPosition++;
}
else
{
CString csTemp;
csTemp = m_csMessage.Mid(nCharacterPosition,2);
nChar = atol((const char *)csTemp);
DrawPattern(RetrievePattern(nChar));
nCharacterPosition += 2;
}
}
else
{
long nTemp2 = m_csMessage.GetAt(nCharacterPosition);
if (nTemp2<-1)
nTemp2 = nTemp2&255;
nChar = g_nASCIItoCode128SubsetAB[m_nCurrentSubset][nTemp2];
DrawPattern(RetrievePattern(nChar));
nCharacterPosition++;
if (m_nCurrentSubset==SUBSETA)
{
if (nChar==100)
m_nCurrentSubset = SUBSETB;
else if (nChar==99)
m_nCurrentSubset = SUBSETC;
}
else if (m_nCurrentSubset==SUBSETB)
{
if (nChar==101)
m_nCurrentSubset = SUBSETA;
else if (nChar==99)
m_nCurrentSubset = SUBSETC;
}
else if (nChar==98)
{
if (m_nCurrentSubset==SUBSETA)
nNextChar = g_nASCIItoCode128SubsetAB[SUBSETB]
[m_csMessage.GetAt(nCharacterPosition)];
else
nNextChar = g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)];
DrawPattern(RetrievePattern(nChar));
nCharacterPosition++;
}
}
}
DrawPattern(RetrievePattern(nCheckDigit));
DrawPattern(RetrievePattern(106));
return;
}
The CCode128::DrawBitmap() function starts out by calculating
the check digit using CCode128::GetCheckDigit(). Then it draws the correct
start character, based on the current subset value m_nCurrentSubset. Then the
code steps through every character in the message and draws the characters by
retrieving the Code 128 characters necessary to draw that message character.
On each pass thru the while loop in CCode128::DrawBitmap(),
an if statement executes two different pieces of code, one for Subset C, and the other for
Subset A and Subset B. If the current subset is Subset C, the code in this if branch
executes for the next message character.
if (m_nCurrentSubset==SUBSETC)
{
if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==101)
{
DrawPattern(RetrievePattern(101));
nCharacterPosition++;
m_nCurrentSubset = SUBSETA;
}
else if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==100)
{
DrawPattern(RetrievePattern(100));
nCharacterPosition++;
m_nCurrentSubset = SUBSETB;
}
else if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==102)
{
DrawPattern(RetrievePattern(100));
nCharacterPosition++;
}
else
{
CString csTemp;
csTemp = m_csMessage.Mid(nCharacterPosition,2);
nChar = atol((const char *)csTemp);
DrawPattern(RetrievePattern(nChar));
nCharacterPosition += 2;
}
}
If you are in Subset C, four things can legally happen. The next character can
switch you to Subset A, the next character can switch you to Subset B, the next
character is FNC1 (the function keys indicate reader specific behavior), or the next
character is a digit. If the next character is a digit, you need to pull off the
next two characters to determine which Code 128 character to draw (remember the
double-density numeric-only nature of Subset C).
If the current subset is Subset A or Subset B, the code in the else branch
executes for the next message character.
else
{
long nTemp2 = m_csMessage.GetAt(nCharacterPosition);
if (nTemp2<-1)
nTemp2 = nTemp2&255;
nChar = g_nASCIItoCode128SubsetAB[m_nCurrentSubset][nTemp2];
DrawPattern(RetrievePattern(nChar));
nCharacterPosition++;
if (m_nCurrentSubset==SUBSETA)
{
if (nChar==100)
m_nCurrentSubset = SUBSETB;
else if (nChar==99)
m_nCurrentSubset = SUBSETC;
}
else if (m_nCurrentSubset==SUBSETB)
{
if (nChar==101)
m_nCurrentSubset = SUBSETA;
else if (nChar==99)
m_nCurrentSubset = SUBSETC;
}
else if (nChar==98)
{
if (m_nCurrentSubset==SUBSETA)
nNextChar = g_nASCIItoCode128SubsetAB[SUBSETB]
[m_csMessage.GetAt(nCharacterPosition)];
else
nNextChar = g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)];
DrawPattern(RetrievePattern(nChar));
nCharacterPosition++;
}
}
The 2 dimensional array g_nASCIItoCode128SubsetAB[2][207] is used to convert
ASCII message characters to Code 128 Subset A and Subset B characters. The
first index of the array is the subset, SUBSETA or SUBSETB, and the second
index is the ASCII value of the current message character. The value at this
position is the Code 128 character for the current message character in the
current subset. The declaration of the array is in CCode128.h and is shown
in the code fragment below.
const long g_nASCIItoCode128SubsetAB[2][207] =
{{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106},
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106}};
Note that the special characters in Subset A and B, characters 95 through 106
(all start characters, FNC1 through FNC 4, all shift characters, and the stop character),
are shifted into the upper ASCII characters by adding 100 to the Code 128 characters. So
these characters are specified in the message using ASCII 195 through 206. ASCII
characters that have no Code 128 equivalent are denoted by a -1. The code now draws the
retrieved Code 128 character using DrawPattern(). Then the code uses an if statement to handle the characters that shift the subset, similar to how
Subset C code does. One new thing is the single character shift (Code 128 character 98),
which toggles between Subset A and Subset B, but only for the character right after the
shift character. This case is handled in the last nested if statement.
Finally, DrawBitmap() finishes up by drawing the previously calculated check digit,
followed by the stop character.
There are 3 private member functions used in DrawBitmap(). The function CCode128::GetCheckDigit() calculates the Code 128 check digit for the message.
CCode128::RetrievePattern() is basically a giant switch statement, retrieving
the pattern for any Code 128 character passed to it. CCode128::DrawPattern()
draws the pattern passed to it, the pattern is a CString in the form of “bssbbbsbbss”
(the character '0' in Subset A and Subset B; the number 16 in Subset C) like the Code 128
character data mentioned above.
CCode128::GetCheckDigit() details
The source code for CCode128::GetCheckDigit() is listed below.
long CCode128::GetCheckDigit()
{
long nSum=0,nCurrentSubset=0,nCode128Char,nNextChar,nWeight,nCharacterPosition;
if (m_nCurrentSubset==SUBSETA)
{
nSum = 103;
nCurrentSubset = SUBSETA;
}
else if (m_nCurrentSubset==SUBSETB)
{
nSum = 104;
nCurrentSubset = SUBSETB;
}
else if (m_nCurrentSubset==SUBSETC)
{
nSum = 105;
nCurrentSubset = SUBSETC;
}
nCharacterPosition = 0;
nWeight = 1;
while (nCharacterPosition<(m_csMessage.GetLength()))
{
if (nCurrentSubset==SUBSETC)
{
if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==101)
{
nCode128Char = 101;
nSum+= (nWeight*nCode128Char);
nCharacterPosition++;
nWeight++;
nCurrentSubset = SUBSETA;
}
else if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==100)
{
nCode128Char = 100;
nSum+= (nWeight*nCode128Char);
nCharacterPosition++;
nWeight++;
nCurrentSubset = SUBSETB;
}
else if (g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)]==102)
{
nCode128Char = 102;
nSum+= (nWeight*nCode128Char);
nCharacterPosition++;
nWeight++;
}
else
{
CString csTemp;
csTemp = m_csMessage.Mid(nCharacterPosition,2);
nCode128Char = atol((const char *)csTemp);
nSum += (nWeight*nCode128Char);
nCharacterPosition += 2;
nWeight++;
}
}
else
{
long nTemp2 = m_csMessage.GetAt(nCharacterPosition);
if (nTemp2<-1)
nTemp2 = nTemp2&255;
nCode128Char = g_nASCIItoCode128SubsetAB[nCurrentSubset][nTemp2];
nSum+= (nWeight*nCode128Char);
nCharacterPosition++;
nWeight++;
if (nCurrentSubset==SUBSETA)
{
if (nCode128Char==100)
nCurrentSubset = SUBSETB;
else if (nCode128Char==99)
nCurrentSubset = SUBSETC;
}
else if (nCurrentSubset==SUBSETB)
{
if (nCode128Char==101)
nCurrentSubset = SUBSETA;
else if (nCode128Char==99)
nCurrentSubset = SUBSETC;
}
else if (nCode128Char==98)
{
if (nCurrentSubset==SUBSETA)
nNextChar = g_nASCIItoCode128SubsetAB[SUBSETB]
[m_csMessage.GetAt(nCharacterPosition)];
else
nNextChar = g_nASCIItoCode128SubsetAB[SUBSETA]
[m_csMessage.GetAt(nCharacterPosition)];
nSum += (nWeight*nNextChar);
nCharacterPosition++;
nWeight++;
}
}
}
return (nSum%103);
}
The CCode::GetCheckDigit() function is very similar to CCode128::DrawBitmap(),
except that when CCode128::DrawBitmap() would draw the character, CCode::GetCheckDigit()
adds the weighted value to a running sum. CCode::GetCheckDigit() then returns the weighted sum
modulus 103 as the check digit.
CCode128::DrawPattern() details
The CCode128::DrawPattern() function draws a single
Code 128 barcode character in the passed device context. The CCode128::DrawPattern() function is listed below.
void CCode128::DrawPattern(CString csPattern)
{
int i,nXPixel,nYPixel;
CDC oDC;
oDC.Attach(m_hDC);
nXPixel = m_nStartingXPixel;
for (i=0;i<csPattern.GetLength();i++)
{
for (nXPixel=m_nStartingXPixel;
nXPixel<m_nStartingXPixel+m_nNarrowBarPixelWidth;
nXPixel++)
{
for (nYPixel=m_nStartingYPixel;
nYPixel<m_nStartingYPixel+m_nPixelHeight;
nYPixel++)
{
if (csPattern.GetAt(i)=='b')
oDC.SetPixelV(nXPixel,nYPixel,COLORBLACK);
else
oDC.SetPixelV(nXPixel,nYPixel,COLORWHITE);
}
}
m_nStartingXPixel+= m_nNarrowBarPixelWidth;
}
oDC.Detach();
return;
}
The CCode128::DrawPattern() function is basically three
loops. The outermost loop loops thru every module in the passed
pattern (bssbbbsbbss). The middle loop loops through every X pixel in
the current module 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 this module is 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, the check digit, and the stop character
to draw the complete Code 128 barcode.
Summary
Thats it for drawing Code 128 barcodes. Some additions to the library
I hope to make in the future involve message error-checking, and adding
UPC,EAN, and 2D barcode symbologies. 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