Click here to Skip to main content
12,509,612 members (50,598 online)
Click here to Skip to main content
Add your own
alternative version


142 bookmarked

Drawing Barcodes in Windows Part 1 - Code 39

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


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:


Pattern (bsbsbsbsb)

























































































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
        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();
        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 
        void BitmapToClipboard();
        void DrawBitmap();
        virtual ~CCode39();
        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;

// assign variable values here

// call LoadData and draw the barcode

Drawing a Barcode to the Clipboard

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

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

// assign variable values here

// call LoadData and 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;

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;
nXAxisDpi = tempDC.GetDeviceCaps(LOGPIXELSX);
nYAxisDpi = tempDC.GetDeviceCaps(LOGPIXELSY);

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

// get final character width
nTemp = m_csMessage.GetLength() + 2;

// add message
m_nFinalBarcodePixelWidth = nTemp * ((3*m_nWideBarPixelWidth) + 

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;

    // draw start character, an asterisk
    // draw each character in the message
    for (i=0;i<m_csMessage.GetLength();i++)

    // draw stop character, also an asterisk


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;

    // attach to the device context

    // 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;
            nTempWidth = m_nWideBarPixelWidth;

        // X value for loop
        for (nXPixel=m_nStartingXPixel; 
            // Y value for loop
            for (nYPixel = m_nStartingYPixel; 
                // if this is a bar
                if (i%2==0)

        // advance the starting position
        m_nStartingXPixel+= nTempWidth;

    // detach from the device context


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.


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.


The Bar Code Book - A Comprehensive Guide To Reading, Printing, Specifying, and Applying Bar Code and Other Machine-Readable Symbols 4th Edition


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

You may also be interested in...


Comments and Discussions

QuestionCode 39 Library Pin
Zacbr21-Apr-12 21:39
memberZacbr21-Apr-12 21:39 
GeneralMy vote of 5 Pin
manoj kumar choubey26-Feb-12 19:36
membermanoj kumar choubey26-Feb-12 19:36 
Questioncode39 Pin
montas31-Jan-12 21:04
membermontas31-Jan-12 21:04 
GeneralMy vote of 5 Pin
totallai19-Oct-10 19:20
membertotallai19-Oct-10 19:20 
Generalurgent Pin
lakshmi priya.t.n29-Jan-09 17:44
memberlakshmi priya.t.n29-Jan-09 17:44 
QuestionSaving to bmp Pin
edvinas_naraveckas24-Aug-06 2:24
memberedvinas_naraveckas24-Aug-06 2:24 
AnswerRe: Saving to bmp Pin
leo_torino21-Feb-07 5:32
memberleo_torino21-Feb-07 5:32 
I create this method to save the data. It works but I don't usually code with C++, any suggestions are appreciated.

// Name:
// BitmapToFile
// Description:
// salva l'immagine del barcode in una bitmap
// Arguments:
// nome del file in cui viene salvata la bitmap con il barcode
// Return:
// 0 on success, -1 on error
// Called by:
// public class interface - called by users of the class
int CCode39::BitmapToFile(CString fileName)
CDC memDC;
CBitmap oBitmap;
int headersize, infosize, bitsize, i;
UINT nWrittenDIBDataSize = 0;


m_hDC = memDC.GetSafeHdc();

// create compatible, correctly sized bitmap

// select our bitmap into the device context
CBitmap * oldbm = memDC.SelectObject(&oBitmap);

// turn area white - stock black bitmap is selected

// draw bitmap into memory device context

oBitmap.GetBitmap(&bmpX); //Get bitmap dimensions
//into BITMAP structure.

//allocate memory for image byte buffer
//GLubyte* bits = malloc(bmpX.bmWidthBytes*bmpX.bmHeight);
BYTE* bmpBuffer = new BYTE[bmpX.bmWidthBytes*bmpX.bmHeight]; //(BYTE*)GlobalAlloc(GPTR, bmpX.bmWidthBytes*bmpX.bmHeight); //(bmpX.bmWidth* 1 + 7) / 8 * abs(bmpX.bmHeight));

//Get the bitmap bits into a structure
//DWORD dwValue=oBitmap.GetBitmapBits((bmpX.bmWidth* 1 + 7) / 8 * abs(bmpX.bmHeight), bmpBuffer);
DWORD dwValue=oBitmap.GetBitmapBits(bmpX.bmWidthBytes*bmpX.bmHeight, bmpBuffer);

headersize = 12; // dimensione dell'header del bitmap
infosize = 40; // dimensione dell'info della bitmap
bitsize = dwValue; // dimensione in byte dell'immagine

info.bmiHeader.biSize = infosize;
info.bmiHeader.biWidth = bmpX.bmWidth; //
info.bmiHeader.biHeight = bmpX.bmHeight; //
info.bmiHeader.biPlanes = bmpX.bmPlanes; //
info.bmiHeader.biBitCount = bmpX.bmBitsPixel; // black and white, 1 byte per pixel
info.bmiHeader.biCompression = 0; // no compression
info.bmiHeader.biSizeImage = bitsize; /* Size of image data */
info.bmiHeader.biXPelsPerMeter = 0; /* X pixels per meter */
info.bmiHeader.biYPelsPerMeter = 0; /* Y pixels per meter */
info.bmiHeader.biClrUsed = 2; /* Number of colors used */
info.bmiHeader.biClrImportant = 0; /* Number of important colors */

// tabella dei colori
// bianco
info.bmiColors[0].rgbBlue = 0;
info.bmiColors[0].rgbGreen = 0;
info.bmiColors[0].rgbRed = 0;
info.bmiColors[0].rgbReserved = 0;
// nero
info.bmiColors[1].rgbBlue = 255;
info.bmiColors[1].rgbGreen = 255;
info.bmiColors[1].rgbRed = 255;
info.bmiColors[1].rgbReserved = 0;

FILE *fp = fopen(fileName, "wb");
if(fp == NULL)
return -1;

// infomazioni di testata
write_word (fp, BF_TYPE); /* bfType BM */
write_dword(fp, headersize + infosize + bitsize); /* bfSize */
write_word (fp, 0); /* bfReserved1 */
write_word (fp, 0); /* bfReserved2 */
write_dword(fp, headersize + infosize); /* bfOffBits, offset per inizio dati dell'immagine */

write_dword(fp, infosize);
write_long (fp, info.bmiHeader.biWidth);
write_long (fp, info.bmiHeader.biHeight);
write_word (fp, info.bmiHeader.biPlanes);
write_word (fp, info.bmiHeader.biBitCount);
write_dword(fp, info.bmiHeader.biCompression);
write_dword(fp, info.bmiHeader.biSizeImage);
write_long (fp, info.bmiHeader.biXPelsPerMeter);
write_long (fp, info.bmiHeader.biYPelsPerMeter);
write_dword(fp, info.bmiHeader.biClrUsed);
write_dword(fp, info.bmiHeader.biClrImportant);

for (i = 0; i < 2; i++ )
fwrite(&info.bmiColors[i].rgbBlue , 1, 1, fp);
fwrite(&info.bmiColors[i].rgbGreen , 1, 1, fp);
fwrite(&info.bmiColors[i].rgbRed , 1, 1, fp);;
fwrite(&info.bmiColors[i].rgbReserved, 1, 1, fp);

for (i = 0; i < bitsize; i++)
fwrite(&bmpBuffer[i], 1, 1, fp);
catch (...)
fclose(fp); // chiude lo stream

// deselect object out of device context

// make sure bitmap not deleted with CBitmap object

// rilascia il buffer con i dati della bitmap
delete [] bmpBuffer;

return -1;

fclose(fp); // chiude lo stream

// deselect object out of device context

// make sure bitmap not deleted with CBitmap object

// rilascia il buffer con i dati della bitmap
delete [] bmpBuffer;

return 0;

catch (...)
return -1;

* 'write_word()' - Write a 16-bit unsigned integer.

static int /* O - 0 on success, -1 on error */
write_word(FILE *fp, /* I - File to write to */
unsigned short w) /* I - Integer to write */
putc(w, fp);
return (putc(w >> 8, fp));

* 'write_dword()' - Write a 32-bit unsigned integer.

static int /* O - 0 on success, -1 on error */
write_dword(FILE *fp, /* I - File to write to */
unsigned int dw) /* I - Integer to write */
putc(dw, fp);
putc(dw >> 8, fp);
putc(dw >> 16, fp);
return (putc(dw >> 24, fp));

* 'write_long()' - Write a 32-bit signed integer.

static int /* O - 0 on success, -1 on error */
write_long(FILE *fp, /* I - File to write to */
int l) /* I - Integer to write */
putc(l, fp);
putc(l >> 8, fp);
putc(l >> 16, fp);
return (putc(l >> 24, fp));
GeneralRe: Saving to bmp Pin
ToodlesTech13-Apr-07 14:05
memberToodlesTech13-Apr-07 14:05 
QuestionHow do I use DrawCode93Barcode from C# ? Pin
PetronelaPutaru15-Aug-06 7:25
memberPetronelaPutaru15-Aug-06 7:25 
GeneralTHX for Sharing. Avoid reinventing the wheel Pin
esman26-Apr-05 0:48
memberesman26-Apr-05 0:48 
Rafael Fernández López6-Dec-04 15:26
memberRafael Fernández López6-Dec-04 15:26 
GeneralGetting Error (Expected ,) Pin
Ashwin Mittal25-Oct-04 0:40
memberAshwin Mittal25-Oct-04 0:40 
GeneralRe: Getting Error (Expected ,) Pin
'¯`°²¤ zee ¤²°`¯'9-Mar-05 20:41
member'¯`°²¤ zee ¤²°`¯'9-Mar-05 20:41 
QuestionHow To Use Pin
Ashwin Mittal24-Oct-04 23:48
memberAshwin Mittal24-Oct-04 23:48 
GeneralPrinting Pin
MasterOfBytes29-Mar-04 5:12
memberMasterOfBytes29-Mar-04 5:12 
GeneralExcelent work Pin
GIN_LARIOS10-Mar-04 12:37
memberGIN_LARIOS10-Mar-04 12:37 
GeneralCODE 39 addendum Pin
Jordan Bell16-May-03 7:59
sussJordan Bell16-May-03 7:59 
GeneralCODE 39 Pin
Jordan Bell16-May-03 6:54
sussJordan Bell16-May-03 6:54 
GeneralLicensing Pin
Neil Van Eps4-Apr-03 12:03
memberNeil Van Eps4-Apr-03 12:03 
GeneralMessage Removed Pin
EXLVBoy28-Mar-03 19:42
sussEXLVBoy28-Mar-03 19:42 
GeneralRe: A free Code 39 Font Pin
pmorelfourrier24-Nov-03 1:58
memberpmorelfourrier24-Nov-03 1:58 
GeneralRe: A free Code 39 Font Pin
ruthertw725-Mar-12 17:29
memberruthertw725-Mar-12 17:29 
GeneralUsing FillRect() Pin
Mike Fair1-Jan-03 11:09
memberMike Fair1-Jan-03 11:09 
GeneralGreat stuff Pin
Anthony_Yio18-Dec-02 17:14
memberAnthony_Yio18-Dec-02 17:14 
GeneralProblems with MM_HIMETRIC Pin
Israel Rosas28-Aug-02 4:45
memberIsrael Rosas28-Aug-02 4:45 
GeneralGreat ! Pin
Braulio Díez26-Jun-02 22:28
memberBraulio Díez26-Jun-02 22:28 
GeneralUse existing fonts instead Pin
Keith Worden17-Jun-02 0:18
memberKeith Worden17-Jun-02 0:18 
GeneralRe: Use existing fonts instead Pin
Jörgen Sigvardsson23-Jun-02 6:13
memberJörgen Sigvardsson23-Jun-02 6:13 
GeneralRe: Use existing fonts instead Pin
Jeff Gortatowsky23-Jun-02 9:14
memberJeff Gortatowsky23-Jun-02 9:14 
GeneralRe: Use existing fonts instead Pin
Todd Smith27-Jun-02 12:22
memberTodd Smith27-Jun-02 12:22 
GeneralRe: Use existing fonts instead Pin
Anonymous17-Apr-03 4:15
sussAnonymous17-Apr-03 4:15 
GeneralRe: Use existing fonts instead Pin
Daniel Zaharia17-Nov-04 1:52
memberDaniel Zaharia17-Nov-04 1:52 
Generali want to get it,THS Pin
jone_lion28-Mar-07 23:45
memberjone_lion28-Mar-07 23:45 
GeneralExcellent Work! Pin
Trinh Minh Cuong16-Jun-02 20:22
memberTrinh Minh Cuong16-Jun-02 20:22 
QuestionFont? Pin
Marc Clifton16-Jun-02 16:28
memberMarc Clifton16-Jun-02 16:28 
AnswerFree Fonts Pin
tupacs0130-Jul-05 12:06
membertupacs0130-Jul-05 12:06 
GeneralCopyright conditions Pin
Gerard Nicol15-Jun-02 15:02
memberGerard Nicol15-Jun-02 15:02 
GeneralRe: Copyright conditions Pin
Mazdak16-Jun-02 6:01
memberMazdak16-Jun-02 6:01 
GeneralRe: Copyright conditions Pin
Brian Delahunty23-Jun-02 3:51
subeditorBrian Delahunty23-Jun-02 3:51 
GeneralRe: Copyright conditions Pin
Mazdak23-Jun-02 4:00
memberMazdak23-Jun-02 4:00 
GeneralRe: Copyright conditions Pin
Brian Delahunty23-Jun-02 4:04
subeditorBrian Delahunty23-Jun-02 4:04 
GeneralRe: Copyright conditions Pin
Brian Delahunty23-Jun-02 3:50
subeditorBrian Delahunty23-Jun-02 3:50 
GeneralRe: Copyright conditions Pin
Anthony_Yio18-Dec-02 17:16
memberAnthony_Yio18-Dec-02 17:16 
GeneralRe: Copyright conditions Pin
Brian Delahunty19-Dec-02 2:04
editorBrian Delahunty19-Dec-02 2:04 
GeneralRe: Copyright conditions Pin
WillemM9-Mar-03 20:59
memberWillemM9-Mar-03 20:59 
GeneralGood Job Pin
Zahirul Haque15-Jun-02 4:03
memberZahirul Haque15-Jun-02 4:03 
GeneralRe: Good Job Pin
Neil Van Eps15-Jun-02 8:58
memberNeil Van Eps15-Jun-02 8:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    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 | Terms of Use | Mobile
Web02 | 2.8.160929.1 | Last Updated 15 Jun 2002
Article Copyright 2002 by Neil Van Eps
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid