Question

I want to create Code39 encoded barcodes from my application.

I know I can use a font for this, but I'd prefer not to as I'd have to register the font on the server and I've had some pretty bad experiences with that.

An example of what I've produced after asking this question is in the answers

Was it helpful?

Solution

This is my current codebehind, with lots of comments:

Option Explicit On
Option Strict On

Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Drawing.Bitmap
Imports System.Drawing.Graphics
Imports System.IO

Partial Public Class Barcode
    Inherits System.Web.UI.Page
    'Sebastiaan Janssen - 20081001 - TINT-30584
    'Most of the code is based on this example: 
    'http://www.atalasoft.com/cs/blogs/loufranco/archive/2008/04/25/writing-code-39-barcodes-with-javascript.aspx-generation.aspx
    'With a bit of this thrown in:
    'http://www.atalasoft.com/cs/blogs/loufranco/archive/2008/03/24/code-39-barcode

    Private _encoding As Hashtable = New Hashtable
    Private Const _wideBarWidth As Short = 8
    Private Const _narrowBarWidth As Short = 2
    Private Const _barHeight As Short = 100

    Sub BarcodeCode39()
        _encoding.Add("*", "bWbwBwBwb")
        _encoding.Add("-", "bWbwbwBwB")
        _encoding.Add("$", "bWbWbWbwb")
        _encoding.Add("%", "bwbWbWbWb")
        _encoding.Add(" ", "bWBwbwBwb")
        _encoding.Add(".", "BWbwbwBwb")
        _encoding.Add("/", "bWbWbwbWb")
        _encoding.Add("+", "bWbwbWbWb")
        _encoding.Add("0", "bwbWBwBwb")
        _encoding.Add("1", "BwbWbwbwB")
        _encoding.Add("2", "bwBWbwbwB")
        _encoding.Add("3", "BwBWbwbwb")
        _encoding.Add("4", "bwbWBwbwB")
        _encoding.Add("5", "BwbWBwbwb")
        _encoding.Add("6", "bwBWBwbwb")
        _encoding.Add("7", "bwbWbwBwB")
        _encoding.Add("8", "BwbWbwBwb")
        _encoding.Add("9", "bwBWbwBwb")
        _encoding.Add("A", "BwbwbWbwB")
        _encoding.Add("B", "bwBwbWbwB")
        _encoding.Add("C", "BwBwbWbwb")
        _encoding.Add("D", "bwbwBWbwB")
        _encoding.Add("E", "BwbwBWbwb")
        _encoding.Add("F", "bwBwBWbwb")
        _encoding.Add("G", "bwbwbWBwB")
        _encoding.Add("H", "BwbwbWBwb")
        _encoding.Add("I", "bwBwbWBwb")
        _encoding.Add("J", "bwbwBWBwb")
        _encoding.Add("K", "BwbwbwbWB")
        _encoding.Add("L", "bwBwbwbWB")
        _encoding.Add("M", "BwBwbwbWb")
        _encoding.Add("N", "bwbwBwbWB")
        _encoding.Add("O", "BwbwBwbWb")
        _encoding.Add("P", "bwBwBwbWb")
        _encoding.Add("Q", "bwbwbwBWB")
        _encoding.Add("R", "BwbwbwBWb")
        _encoding.Add("S", "bwBwbwBWb")
        _encoding.Add("T", "bwbwBwBWb")
        _encoding.Add("U", "BWbwbwbwB")
        _encoding.Add("V", "bWBwbwbwB")
        _encoding.Add("W", "BWBwbwbwb")
        _encoding.Add("X", "bWbwBwbwB")
        _encoding.Add("Y", "BWbwBwbwb")
        _encoding.Add("Z", "bWBwBwbwb")
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        BarcodeCode39()
        Dim barcode As String = String.Empty
        If Not IsNothing(Request("barcode")) AndAlso Not (Request("barcode").Length = 0) Then
            barcode = Request("barcode")
            Response.ContentType = "image/png"
            Response.AddHeader("Content-Disposition", String.Format("attachment; filename=barcode_{0}.png", barcode))

            'TODO: Depending on the length of the string, determine how wide the image will be
            GenerateBarcodeImage(250, 140, barcode).WriteTo(Response.OutputStream)
        End If
    End Sub

    Protected Function getBCSymbolColor(ByVal symbol As String) As System.Drawing.Brush
        getBCSymbolColor = Brushes.Black
        If symbol = "W" Or symbol = "w" Then
            getBCSymbolColor = Brushes.White
        End If
    End Function

    Protected Function getBCSymbolWidth(ByVal symbol As String) As Short
        getBCSymbolWidth = _narrowBarWidth
        If symbol = "B" Or symbol = "W" Then
            getBCSymbolWidth = _wideBarWidth
        End If
    End Function

    Protected Overridable Function GenerateBarcodeImage(ByVal imageWidth As Short, ByVal imageHeight As Short, ByVal Code As String) As MemoryStream
        'create a new bitmap
        Dim b As New Bitmap(imageWidth, imageHeight, Imaging.PixelFormat.Format32bppArgb)

        'create a canvas to paint on
        Dim canvas As New Rectangle(0, 0, imageWidth, imageHeight)

        'draw a white background
        Dim g As Graphics = Graphics.FromImage(b)
        g.FillRectangle(Brushes.White, 0, 0, imageWidth, imageHeight)

        'write the unaltered code at the bottom
        'TODO: truely center this text
        Dim textBrush As New SolidBrush(Color.Black)
        g.DrawString(Code, New Font("Courier New", 12), textBrush, 100, 110)

        'Code has to be surrounded by asterisks to make it a valid Code39 barcode
        Dim UseCode As String = String.Format("{0}{1}{0}", "*", Code)

        'Start drawing at 10, 10
        Dim XPosition As Short = 10
        Dim YPosition As Short = 10

        Dim invalidCharacter As Boolean = False
        Dim CurrentSymbol As String = String.Empty

        For j As Short = 0 To CShort(UseCode.Length - 1)
            CurrentSymbol = UseCode.Substring(j, 1)
            'check if symbol can be used
            If Not IsNothing(_encoding(CurrentSymbol)) Then
                Dim EncodedSymbol As String = _encoding(CurrentSymbol).ToString

                For i As Short = 0 To CShort(EncodedSymbol.Length - 1)
                    Dim CurrentCode As String = EncodedSymbol.Substring(i, 1)
                    g.FillRectangle(getBCSymbolColor(CurrentCode), XPosition, YPosition, getBCSymbolWidth(CurrentCode), _barHeight)
                    XPosition = XPosition + getBCSymbolWidth(CurrentCode)
                Next

                'After each written full symbol we need a whitespace (narrow width)
                g.FillRectangle(getBCSymbolColor("w"), XPosition, YPosition, getBCSymbolWidth("w"), _barHeight)
                XPosition = XPosition + getBCSymbolWidth("w")
            Else
                invalidCharacter = True
            End If
        Next

        'errorhandling when an invalidcharacter is found
        If invalidCharacter Then
            g.FillRectangle(Brushes.White, 0, 0, imageWidth, imageHeight)
            g.DrawString("Invalid characters found,", New Font("Courier New", 8), textBrush, 0, 0)
            g.DrawString("no barcode generated", New Font("Courier New", 8), textBrush, 0, 10)
            g.DrawString("Input was: ", New Font("Courier New", 8), textBrush, 0, 30)
            g.DrawString(Code, New Font("Courier New", 8), textBrush, 0, 40)
        End If

        'write the image into a memorystream
        Dim ms As New MemoryStream

        Dim encodingParams As New EncoderParameters
        encodingParams.Param(0) = New EncoderParameter(Encoder.Quality, 100)

        Dim encodingInfo As ImageCodecInfo = FindCodecInfo("PNG")

        b.Save(ms, encodingInfo, encodingParams)

        'dispose of the object we won't need any more
        g.Dispose()
        b.Dispose()

        Return ms
    End Function

    Protected Overridable Function FindCodecInfo(ByVal codec As String) As ImageCodecInfo
        Dim encoders As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders
        For Each e As ImageCodecInfo In encoders
            If e.FormatDescription.Equals(codec) Then Return e
        Next
        Return Nothing
    End Function
End Class

OTHER TIPS

If you choose Code39, you could probably code up from this code I wrote

http://www.atalasoft.com/cs/blogs/loufranco/archive/2008/03/24/code-39-barcode-generation.aspx

I wrote it to use our toolkit for image generation, but you could rewrite it to use .NET Image/Graphics pretty easily.

I don't know about libraries -- all of the barcode work I've done has been with barcode fonts. Check out free 3-of-9 if you're using the "3 of 9" format.

Caveats of 3-of-9:

make sure all text is in upper case start and end each barcode with an asterisk

Here's an open source barcode rendering library for .NET languages: http://www.codeplex.com/BarcodeRender

It can render some usual encodings.

The license looks benign, and it seems to be usable in both open source and commercial apps (however, IANAL, you might want to check its license yourself.)

Here's another one, also open source, using the Apache 2.0 license: http://sourceforge.net/projects/onecode/

Generally, when you know from the start you're looking for open source components, it's better to bypass Google and directly start searching on SourceForge (it's got a wonderful filtering system for search results, you can filter by language, which is probably of interest to you) or on Microsoft's CodePlex (where choice is usually more limited, but there you go.)

are you printing to a standard printer or an actual barcode printer (zebra or datamax)? both zebra and datamax have their own scripting languages - actually more like markup languages. ZPL and DPL respectively. I like zebra's more and their documentation is much cleaner.

If you don't have a legitimate barcode printer, I suggest purchasing one and do the following.... (it'll be much cleaner than trying to work out building little image blocks and painting them to simulate a barcode font)

both allow you great flexibility and you can let the printer handle creating the actual barcode image.

have your program send a customized ZPL/DPL script that includes the values that you want to have printed as barcodes to the printer via ftp. basically, you just "put" a text file that contains the script to the IP of the printer and the printer takes care of the font.

The iTextSharp library, while ostensibly for creating creating PDFs, also has a barcode generation library that includes Code39.

Once you add a reference to the DLL, it's as simple as:

Barcode39 code39 = new Barcode39();
code39.Code = "Whatever You're Encoding";

Oops, that's C#, but you get the idea. Once created, you can render an image in just about any image format and use it as you wish.

If you render client side then the font can reside on a workstation. This way you can use 3-of-9. I've used 3-of-9 in several projects and the simplest solution for you.

Here is an example of how to generate Code39 barcodes in vb.net. I tested It now and it works.

 Public Class code39
    Private bitsCode As ArrayList

    Public Sub New()
        bitsCode = New ArrayList
        bitsCode.Add(New String(3) {"0001101", "0100111", "1110010", "000000"})
        bitsCode.Add(New String(3) {"0011001", "0110011", "1100110", "001011"})
        bitsCode.Add(New String(3) {"0010011", "0011011", "1101100", "001101"})
        bitsCode.Add(New String(3) {"0111101", "0100001", "1000010", "001110"})
        bitsCode.Add(New String(3) {"0100011", "0011101", "1011100", "010011"})
        bitsCode.Add(New String(3) {"0110001", "0111001", "1001110", "011001"})
        bitsCode.Add(New String(3) {"0101111", "0000101", "1010000", "011100"})
        bitsCode.Add(New String(3) {"0111011", "0010001", "1000100", "010101"})
        bitsCode.Add(New String(3) {"0110111", "0001001", "1001000", "010110"})
        bitsCode.Add(New String(3) {"0001011", "0010111", "1110100", "011010"})
    End Sub

    Public Function Generate(ByVal Code As String) As Image
        Dim a As Integer = 0
        Dim b As Integer = 0
        Dim imgCode As Image
        Dim g As Graphics
        Dim i As Integer
        Dim bCode As Byte()
        Dim bitCode As Byte()
        Dim tmpFont As Font

        If Code.Length <> 12 Or Not IsNumeric(Code.Replace(".", "_").Replace(",", "_")) Then Throw New Exception("Le code doit être composé de 12 chiffres")

        ReDim bCode(12)
        For i = 0 To 11
            bCode(i) = CInt(Code.Substring(i, 1))
            If (i Mod 2) = 1 Then
                b += bCode(i)
            Else
                a += bCode(i)
            End If
        Next

        i = (a + (b * 3)) Mod 10
        If i = 0 Then
            bCode(12) = 0
        Else
            bCode(12) = 10 - i
        End If
        bitCode = getBits(bCode)

        tmpFont = New Font("times new roman", 14, FontStyle.Regular, GraphicsUnit.Pixel)
        imgCode = New Bitmap(110, 50)
        g = Graphics.FromImage(imgCode)
        g.Clear(Color.White)

        g.DrawString(Code.Substring(0, 1), tmpFont, Brushes.Black, 2, 30)
        a = g.MeasureString(Code.Substring(0, 1), tmpFont).Width

        For i = 0 To bitCode.Length - 1
            If i = 2 Then
                g.DrawString(Code.Substring(1, 6), tmpFont, Brushes.Black, a, 30)
            ElseIf i = 48 Then
                g.DrawString(Code.Substring(7, 5) & bCode(12).ToString, tmpFont, Brushes.Black, a, 30)
            End If

            If i = 0 Or i = 2 Or i = 46 Or i = 48 Or i = 92 Or i = 94 Then
                If bitCode(i) = 1 Then 'noir
                    g.DrawLine(Pens.Black, a, 0, a, 40)
                    a += 1
                End If
            Else
                If bitCode(i) = 1 Then 'noir
                    g.DrawLine(Pens.Black, a, 0, a, 30)
                    a += 1
                Else 'blanc
                    a += 1
                End If
            End If
        Next
        g.Flush()
        Return imgCode
    End Function

    Private Function getBits(ByVal bCode As Byte()) As Byte()
        Dim i As Integer
        Dim res As Byte()
        Dim bits As String = "101"
        Dim cle As String = bitsCode(bCode(0))(3)
        For i = 1 To 6
            bits &= bitsCode(bCode(i))(CInt(cle.Substring(i - 1, 1)))
        Next
        bits &= "01010"
        For i = 7 To 12
            bits &= bitsCode(bCode(i))(2)
        Next
        bits += "101"
        ReDim res(bits.Length - 1)
        For i = 0 To bits.Length - 1
            res(i) = Asc(bits.Chars(i)) - 48
        Next
        Return res
    End Function

End Class

Instead of using barcode font, i would prefer a .net barcode generator component. Below is a vb.net sample for creating Code 39 barcode.

Imports System.IO
Imports PQScan.BarcodeCreator

Namespace BarcodeGeneratorVB
Class Program
    Private Shared Sub Main(args As String())
        Dim barcode As New Barcode()

        barcode.Data = "www.pqscan.com"
        barcode.BarType = BarCodeType.Code39
        barcode.Width = 300
        barcode.Height = 100

        barcode.CreateBarcode("code39-vb.jpeg")
    End Sub
End Class
End Namespace

At my last job I worked with a couple different libraries in vb.net for this. We had one, and moved to a different one. I can't remember their names (I'd recognize them again if I saw them), but I do know that both were for-pay, we evaluated several different components at the time of the switch, and I think that included a free one. We were a very small shop and very cost sensitive, so if the free component were any good at all you can bet we would have used it (I think we needed 128b support, and it only handled code39).

I also remember that reason we switched was that it was at the same time we moved from .Net 1.1 to .Net 2.0, and the first component was too slow making the transition.

So, in summary, there is something out there, but it wasn't any good 3 years ago. Hopefully someone else can come along and fill in some actual names.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top