Click here to Skip to main content
Click here to Skip to main content

Drawing Barcodes in Windows Part 4 - Code 93

, 14 Jun 2002
Rate this:
Please Sign up or sign in to vote.
An article on drawing Code 93 barcodes to the screen or to the clipboard

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 93 basics

This fourth article is about drawing Code 93 barcodes on the Windows screen. Before I start discussing the code, we'll need to know some basic facts about the Code 93 barcode symbology. Code 93 is an alpha-numeric code that can encode all ASCII characters through the use of four "shift" characters. Code 93 characters consist of 3 bars and 3 spaces, which are built by using 9 modules each of which can be black or white. Because all of the characters are built from the same 9 modules, there really is only one element width that needs to be defined, the module width. The bar/space patterns for the 47 data characters and the start/stop codes are listed below. Each pattern is 9 characters (modules) long, and is either 'b' (that module is a bar) or 's' (that module is a space). Note that the start and stop characters are the same except that the stop character adds a "termination bar".

Character

Pattern

0

bsssbsbss

1

bsbssbsss

2

bsbsssbss

3

bsbssssbs

4

bssbsbsss

5

bssbssbss

6

bssbsssbs

7

bsbsbssss

8

bsssbssbs

9

bssssbsbs

A

bbsbsbsss

B

bbsbssbss

C

bbsbsssbs

D

bbssbsbss

E

bbssbssbs

F

bbsssbsbs

G

bsbbsbsss

H

bsbbssbss

I

bsbbsssbs

J

bssbbsbss

K

bsssbbsbs

L

bsbsbbsss

M

bsbssbbss

N

bsbsssbbs

O

bssbsbbss

P

bsssbsbbs

Q

bbsbbsbss

R

bbsbbssbs

S

bbsbsbbss

T

bbsbssbbs

U

bbssbsbbs

V

bbssbbsbs

W

bsbbsbbss

X

bsbbssbbs

Y

bssbbsbbs

Z

bssbbbsbs

-

bssbsbbbs

PERIOD

bbbsbsbss

SPACE

bbbsbssbs

$

bbbssbsbs

/

bsbbsbbbs

bsbbbsbbs

%

bbsbsbbbs

SHIFT1

bssbssbbs

SHIFT2

bbbsbbsbs

SHIFT3

bbbsbsbbs

SHIFT4

bssbbssbs

START

bsbsbbbbs

STOP

bsbsbbbbsb

Each Code 93 barcode has two check characters (referred to as "C" and "K") that immediately precede the stop character. Check character "C" is a modulo 47 sum of the weighted message characters, weighted by the repeating sequence {1,2,3....19,20} weighted from right to left. Check character "K" is a modulo 47 sum of the weighted message characters , weighted by the repeating sequence {1,2,...14,15} weighted from right to left beginning with the previously calculated check character "C". An example of the check digit calculations is shown below, using the message "DATA".

Data

D

A

T

A

“C”

“K”

Data Values

13

10

29

10



“C” Weight

4

3

2

1



“K” Weight

5

4

3

2

1


To calculate "C" first find the sum of products: (13*4)+(10*3)+(29*2)+(10*1) = 150. Divide 150 by 47 to get 3 with a remainder of 9. The value of "C" is 9 which corresponds to the digit 9.

To calculate "K" first find the sum of products: (13*5)+(10*4)+(29*3)+(10*2)+(9*1) = 221. Divide 221 by 47 to get 4 with a remainder of 33. The value of "K" is 33 which corresponds to the character 'X'.

A picture of the complete Code 93 barcode is show below.

Note that the barcode reader uses the "C" and "K" check digits to decode the barcode, but does not transmit them.

Full ASCII Code 93

This library implements full ASCII Code 93. The table below shows how produce all ASCII characters using the 47 character Code 93 character set.

ASCII value

ASCII char

Code 93 sequence

ASCII value

ASCII char

Code 93 sequence

ASCII value

ASCII char

Code 93 sequence

ASCII value

ASCII char

Code 93 sequence

0

NUL

<S2>U

32

space

Space

64

@

<S2>V

96

`

<S2>W

1

SOH

<S1>A

33

!

<S3>A

65

A

A

97

a

<S4>A

2

STX

<S1>B

34

<S3>B

66

B

B

98

b

<S4>B

3

ETX

<S1>C

35

#

<S3>C

67

C

C

99

c

<S4>C

4

EOT

<S1>D

36

$

$

68

D

D

100

d

<S4>D

5

ENQ

<S1>E

37

%

%

69

E

E

101

e

<S4>E

6

ACK

<S1>F

38

&

<S3>F

70

F

F

102

f

<S4>F

7

BEL

<S1>G

39

'

<S3>G

71

G

G

103

g

<S4>G

8

BS

<S1>H

40

(

<S3>H

72

H

H

104

h

<S4>H

9

HT

<S1>I

41

)

<S3>I

73

I

I

105

i

<S4>I

10

LF

<S1>J

42

*

<S3>J

74

J

J

106

j

<S4>J

11

VT

<S1>K

43

+

+

75

K

K

107

k

<S4>K

12

FF

<S1>L

44

comma

<S3>L

76

L

L

108

l

<S4>L

13

CR

<S1>M

45

hyphen

Hyphen

77

M

M

109

m

<S4>M

14

SO

<S1>N

46

period

Period

78

N

N

110

n

<S4>N

15

SI

<S1>O

47

/

/

79

O

O

111

o

<S4>O

16

DLE

<S1>P

48

0

0

80

P

P

112

p

<S4>P

17

DC1

<S1>Q

49

1

1

81

Q

Q

113

q

<S4>Q

18

DC2

<S1>R

50

2

2

82

R

R

114

r

<S4>R

19

DC3

<S1>S

51

3

3

83

S

S

115

s

<S4>S

20

DC4

<S1>T

52

4

4

84

T

T

116

t

<S4>T

21

NAK

<S1>U

53

5

5

85

U

U

117

u

<S4>U

22

SYN

<S1>V

54

6

6

86

V

V

118

v

<S4>V

23

ETB

<S1>W

55

7

7

87

W

W

119

w

<S4>W

24

CAN

<S1>X

56

8

8

88

X

X

120

x

<S4>X

25

EM

<S1>Y

57

9

9

89

Y

Y

121

y

<S4>Y

26

SUB

<S1>Z

58

:

<S3>Z

90

Z

Z

122

z

<S4>Z

27

ESC

<S2>A

59

;

<S2>F

91

[

<S2>K

123

{

<S2>P

28

FS

<S2>B

60

<

<S2>G

92

\

<S2>L

124

|

<S2>Q

29

GS

<S2>C

61

equal

<S2>H

93

]

<S2>M

125

}

<S2>R

30

RS

<S2>D

62

>

<S2>I

94

^

<S2>N

126

~

<S2>S

31

US

<S2>E

63

?

<S2>J

95

_

<S2>O

127

DEL

<S2>T

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 CCode93 class

The CCode93 class is the class to implement to draw a Code93 barcode. The class declaration is listed below.

class CCode93 : public CBarcode  
{
public:
    CCode93();
    virtual ~CCode93();
    void BitmapToClipboard();
    void DrawBitmap();
private:
    void    ASCIItoCode93Sequence( long nASCIINumber,
                                    long *nFirstNumber, 
                                    long *nSecondNumber);
    void    DrawCheckDigits();
    void    DrawPattern(CString csPattern);
    CString    RetrievePattern( long 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;
HDC            pDC;
long            nStartingXPixel, nStartingYPixel;
CCode93            oBarcode;

// assign variable values here

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

Drawing a Barcode to the Clipboard

The following code snipet is an example using BitmapToClipboard().

HDC            hDC = NULL;
double            dNarrowbar,dHeight;
CCode93            oBarcode;

// assign variable values here

// call LoadData and BitmapToClipboard()
oBarcode.LoadData(csMessage,dNarrowBar,dHeight,hDC,0,0);
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 93 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 93 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 93 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.

// 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);

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 93, 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 39 code excerpt is listed below.

nTemp = m_csMessage.GetLength();

m_nFinalBarcodePixelWidth = (((nTemp+4)*9)+1)*m_nNarrowBarPixelWidth;

This code computes the width of a Code 93 barcode by taking the message length, adding 4 to it (the start code, the "C" check digit, the "K" check digit, and the stop code), multiplying the sum by 9 (for the 9 modules), adding 1 to that product (for the stop code termination bar), and multiplying that final sum by the module width in pixels.

CCode93::DrawBitmap() details

The DrawBitmap() function is where each message character is drawn.. A listing of the CCode93::DrawBitmap() function is listed below.

void CCode93::DrawBitmap()
{
    long        i,nFirstNumber,nSecondNumber;
    CString        csCurrentPattern;

    // draw start character
    DrawPattern(RetrievePattern(47));
    
    // draw each character in the message
    for (i=0;i<m_csMessage.GetLength();i++)
    {
        // get current ASCII character
        ASCIItoCode93Sequence((long)m_csMessage.GetAt(i),
                               &nFirstNumber,&nSecondNumber);

        DrawPattern(RetrievePattern(nFirstNumber));
        if (nSecondNumber!=-1)
            DrawPattern(RetrievePattern(nSecondNumber));
    }

    // add the check digit characters
    DrawCheckDigits();

    // draw stop character
    DrawPattern(RetrievePattern(48));

    return;
}

The CCode93::DrawBitmap() function starts out by drawing the start code. Then the code steps through every character in the message and draws the characters by retrieving the Code 93 characters necessary to draw that message character (remember this could be one or two Code 93 characters). Then the two check digits are drawn, followed finally by the stop character. There are 4 private member functions used in DrawBitmap(). The function ASCIItoCode93Sequence() returns the two numbers necessary to draw the passed ASCII character in the two variables nFirstNumber and nSecondNumber. If the current message character can be drawn using one Code 93 character the nSecondNumber variable is set to -1, and the if statement skips drawing the second Code 93 character. CCode93::RetrievePattern() is basically a giant switch statement, retrieving the pattern for any Code 93 character passed to it. CCode93::DrawPattern() draws the pattern passed to it, the pattern is a CString in the form of “bsssbsbss” (the character '0') like the Code 93 character data mentioned above.

CCode93::DrawPattern() details

The CCode93::DrawPattern() function draws a single Code 93 barcode character in the passed device context. The CCode93::DrawPattern() function is listed below.

void CCode93::DrawPattern( CString csPattern )
{
    int            i,nXPixel,nYPixel;
    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++)
    {
        // X value for loop
        for (nXPixel=m_nStartingXPixel;
             nXPixel<m_nStartingXPixel+m_nNarrowBarPixelWidth;
             nXPixel++)
        {
            // Y value for loop
            for (nYPixel=m_nStartingYPixel;
                 nYPixel<m_nStartingYPixel+m_nPixelHeight;
                 nYPixel++)
            {
                // if this is a bar
                if (csPattern.GetAt(i)=='b')
                    oDC.SetPixelV(nXPixel,nYPixel,COLORBLACK);
                else
                    oDC.SetPixelV(nXPixel,nYPixel,COLORWHITE);
            }
        }

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

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

The CCode93::DrawPattern() function is basically three loops. The outermost loop loops thru every module in the passed pattern (bsssbsbss). 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 two check digits, and the stop character to draw the complete Code 93 barcode.

Summary

Thats it for drawing Code93 barcodes. Part V of the series deals with drawing Code 128 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

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

Neil Van Eps
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
QuestionCode 93 barcode generator PinmemberZacbr13-May-12 14:18 
GeneralMy vote of 5 Pinmembermanoj kumar choubey26-Feb-12 20:04 
GeneralReading code93 PingroupPLFaBiaN511-Jun-10 5:19 
QuestionGrid correction? PinmemberJason McBurney6-Apr-09 9:14 
QuestionBug Calculating m_nFinalBarcodePixelWidth? PinmemberPopeye Doyle Murray15-May-03 6:03 
GeneralLicensing PinmemberNeil Van Eps4-Apr-03 12:05 
GeneralBug in DrawCheckDigits Pinmembererand10-Feb-03 23:44 
GeneralVery Good! Pinmemberzhangzq7115-Jun-02 2:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 15 Jun 2002
Article Copyright 2002 by Neil Van Eps
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid