Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Drawing Barcodes in Windows Part 2 - Codebar

0.00/5 (No votes)
14 Jun 2002 1  
An article on drawing Codebar 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.

Codabar basics

This second article is about drawing Codabar barcodes on the Windows screen. Before I start discussing the code, we'll need to know some basic facts about the Codabar barcode symbology. Codabar is used today in blood bank, library, and certain express air parcel applications. Codabar has two different element widths, wide and narrow, which are usually specified by giving the narrow width and the narrow/wide ratio. Each Codabar character has four bars seperated by three spaces for a total of seven elements. Because of the symbologies structure, there are two different character widths. Each character is followed by an intercharacter gap, generally the width of a narrow element. The 20 characters in the Codabar symbology are listed below:

Character

Pattern (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

A Codabar message begins/ends with an a start/stop character (A-D). Additional data can be encoded by the choice of start/stop characters. A sample Codabar message “012345” is shown 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 CRationalCodabar class

The CRationalCodabar class is the class to implement to draw a Codabar barcode. The class declaration is listed below.

class CRationalCodabar : public CBarcode 
{
    public:
        void DrawBitmap();
        void BitmapToClipboard();
        CRationalCodabar();
        virtual ~CRationalCodabar();

    private:
        CString RetrievePattern(char c);
        void DrawPattern(CString csPattern);
};

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;
CRationalCodabar oBarcode;

// assign variable values here


// call LoadData and draw the barcode

oBarcode.LoadData(csMessage,dNarrowBar,dHeight,pDC, 
                  nStartingXPixel,nStartingYPixel,dRatio);
oBarcode.DrawBitmap();

Drawing a Barcode to the Clipboard

The following code snippet is an example using 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();

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 Codabar 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.

// 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.

Next you can calculate the final barcode pixel width, this operation is symbology specific and the Codabar code excerpt is listed below.

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

This code loops through each message character, picking one of the two available widths for each character, and keeps a sum of the total barcode width.

CRationalCodabar::DrawBitmap() details

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

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;
}

The CRationalCodabar::DrawBitmap() function has no way of knowing which start or stop code you want, so they are assumed to be part of the message. The the code steps through every character in the message and draws each character. There are two private member functions that are used here. CRationalCodabar ::DrawPattern() draws the pattern passed to it, the pattern is a CString in the form of “nnnnwwn” (the character '1') like the character data mentioned above. CRationalCodabar::RetrievePattern() is basically a giant switch statement, retrieving the pattern for any legal Codabar character passed to it. Note that each character pattern returned from CRationalCodabar::RetrievePattern() has an extra “n” tacked on to the end to add the inter-character gap.

CRationalCodabar::DrawPattern() details

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

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;
}

The CRationalCodabar::DrawPattern() function is basically three loops. The outermost loop loops thru every character in the pattern (nnnnwwn). 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 Codabar barcode.

Summary

Thats it for drawing Codabar barcodes. Part III of the series deals with drawing I2of5 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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here