Barcodes in LBI

I’ve been getting hits lately for “barcodes in LBI”, so I thought I’d mention how we do barcodes in LBI. When I say LBI, I’m really just talking about Reporting Services and how to do barcodes in the context of Crystal Reports.

The most important thing to consider is what format your reports are going to be delivered in. There are two choices: PDF and any other file format (including crystal). When delivering the report in PDF format, it is not necessary for the report user to have any special setup or configurations. Any other format would probably require special setup. I say “probably” because I’m not certain. The only method I’m aware of to generate barcodes in Crystal is by using a barcode font. This means that for any delivery format besides PDF, the end user must have the barcode font installed on their local machine. However, there is a way to use CSS in Crystal reports, so it’s entirely possible that you may be able to generate a barcode that way, like I discuss here, but I’ve never tried it using it like that.

We currently use two different types of barcodes: Code39 and Code128. The Code39 barcode is from Azalea and the Code128 is from Dobson software.

Despite what the Azalea site says, Code39 does not require check digits. Many scanners also don’t require that you put the leading and trailing * characters (at least the three different brands of scanners that we have do not require them). This means that you can just use the Azalea font for the Crystal field and that’s it. I honestly can’t say whether we paid for Code39 or not as I’m not the one who implemented it’s use. For more on Code39, see the Wikipedia page.

Code128, on the other hand, is fairly complicated. It has three different flavors (A,B,C) and requires different characteristics depending on the type. Using this font in Crystal requires that you use a formula (or UFL) to properly generate the barcode. See the Wikipedia page for more info on Code128. The formula I use to generate correct Code128 is below.

(Note: this code generates a Code128B and uses the Code128bWin font from Dobson Software. This particular code happens to also be from one of the reports – W4 – that is part of our new hire onboarding application. It is generated from LBI and the barcode is read in ImageNow where it’s linked and filed automatically by Recognition Agent.)

Local StringVar strText := "Payroll_Employee" & "^" & {xml.ssn} & "^" {xml.EMPLOYEE_ID} & "^" & {xml.last_name} & "^" & {xml.first_name} & "^PR_Federal_W4";
//Replacing spaces in text string.  For some reason the barcode is unreadable when I leave them in
strText := Replace (strText, " ", "+");
Local NumberVar intRunTotal := 104;  //Start code for Bset of Code128
Local NumberVar intLength := Length(strText);
Local Numbervar i;
Local Numbervar intCodeValue;   //Code128 value of current character
Local Numbervar intCheckValue;  //Check value in Code128
Local NumberVar intCheckValueA;  //Hold ASCII value for Code128 value

for i:= 1 to intLength Do
    Local StringVar strChar := strText[i];
    //Get code128 value
    If ASC(strChar) = 128 Then
        intCodeValue := 0
        If ASC(strChar) >= 33 And ASC(strChar) = 1 And intCheckValue <= 94 Then
        	intCheckValueA := intCheckValue + 32
        	intCheckValueA := intCheckValue + 50

    //Add to running total for check char
    intRunTotal := intRunTotal + (intCodeValue * i)

//Get check value which is remainder of running total / 103
intCheckValue := intRunTotal Mod 103;

//Convert back to ASCII to display
If intCheckValue = 0 then
    intCheckValueA := 128
    If intCheckValue >= 1 And intCheckValue <= 94 Then
        intCheckValueA := intCheckValue + 32
        intCheckValueA := intCheckValue + 50
strText := chr(154) & strText & chr(intCheckValueA) & chr(156)



Javascript Code128 Barcode

This is a departure from my usual postings. I’m not going to give a whole lot of background or write up on it. Instead I’m just going to give you the code. You either need code that can generate code128 or you don’t. The code provided here should give you a working page that will generate a Code128 barcode in either horizontal or vertical format. I’ve tested the generation in IE (7+) and Firefox (3.5) and tested the reading of the barcode with the AABBY OCR software.

My reason for pursuing this was because I’ve got several different methods to read barcodes, but not many to generate them. One involves special fonts, which is fine for server based generation, but when I need to generate a barcode based on data that is on a web page, the options were lacking.

This code is derived from the code at: Code128. The updates that I’m providing are:

  1. Original Page does not display barcode in IE 7+. This has been fixed.
  2. Separated CSS and Javascript into separate files
  3. Removed requirement of base2.js library (I didn’t see any value-add to include this large of a library)
  4. Added code to support Vertical Barcode (top to bottom)
  5. The original form generation was a bit odd and convoluted, so it has been simplified

For the most part, except for removing the base2.js dependencies, I left the javascript in-tact. I must say that some of it is exceptionally well-done by the original author.

To switch between vertical layout and horizontal layout, change the css class of the div element with the “barcode” id in the base128.htm file. Please note that the vertical barcode sets a z-index of 99. This means that you should set a left margin for your base elements, otherwise the barcode will float over the top and be unreadable.

***The HTML file: code128.htm

<link rel="StyleSheet" href="barcode.css" type="text/css" />
<script src="barcode.js"></script>
<script type="text/javascript">
function encode()
  var strValue = document.getElementById("barcode_input").value;
  var strBarcodeHTML = code128(strValue);
  document.getElementById("barcode").innerHTML = strBarcodeHTML;
<div class="barcode128v" id="barcode"></div>
<br /><br />
<input style="margin-left:45px" type="text" id="barcode_input"></input>
<input type="button" value="Encode" onclick="encode()"/>

***The CSS file: barcode.css

                CODE 128 styles

/* ------------------- Horizontal Barcode -------------------- */
.barcode128h  {
    clear: both;
    padding: 0 10px /*quiet zone*/;
    overflow: auto;
    height: 0.35in   /*size*/;

.barcode128h div {
    /*float: left;*/
    height: 0.2 in  /*size*/;
    display: inline;

.barcode128h label {
    clear: both;
    display: block;
    text-align: center;
    font: 0.125in/100% helvetica /*size*/;

.barcode128h .bar1 { border-left: 1px solid black }
.barcode128h .bar2 { border-left: 2px solid black }
.barcode128h .bar3 { border-left: 3px solid black }
.barcode128h .bar4 { border-left: 4px solid black }
.barcode128h .space0 { margin-right: 0px }
.barcode128h .space1 { margin-right: 1px }
.barcode128h .space2 { margin-right: 2px }
.barcode128h .space3 { margin-right: 3px }
.barcode128h .space4 { margin-right: 4px }

/* ------------------- Vertical Barcode --------------------- */
.barcode128v  {
    /*float: top;*/
    clear: left;
    position: absolute;
    padding: 10 0px /*quiet zone*/;
    overflow: auto;
    width: 30px  /*size*/;
    z-index: 99;


.barcode128v div {
    float: top;
    display: block;
    /*width: 20px;

.barcode128v label {
    clear: right;
    display: block;
    text-align: center;
    font: 0.125in/100% helvetica /*size*/;
    writing-mode: tb-rl;
    filter: flipH() flipV();

.barcode128v .bar1 { border-top: 1px solid black; z-index: 99;}
.barcode128v .bar2 { border-top: 2px solid black; z-index: 99;}
.barcode128v .bar3 { border-top: 3px solid black; z-index: 99;}
.barcode128v .bar4 { border-top: 4px solid black; z-index: 99;}
.barcode128v .space0 { margin-bottom: 0px; z-index: 99; }
.barcode128v .space1 { margin-bottom: 1px; z-index: 99; }
.barcode128v .space2 { margin-bottom: 2px; z-index: 99; }
.barcode128v .space3 { margin-bottom: 3px; z-index: 99; }
.barcode128v .space4 { margin-bottom: 4px; z-index: 99; }

***The Javascript file: barcode.js

BARS       = [212222,222122,222221,121223,121322,131222,122213,122312,132212,221213,221312,231212,112232,122132,122231,113222,123122,123221,223211,221132,221231,213212,223112,312131,311222,321122,321221,312212,322112,322211,212123,212321,232121,111323,131123,131321,112313,132113,132311,211313,231113,231311,112133,112331,132131,113123,113321,133121,313121,211331,231131,213113,213311,213131,311123,311321,331121,312113,312311,332111,314111,221411,431111,111224,111422,121124,121421,141122,141221,112214,112412,122114,122411,142112,142211,241211,221114,413111,241112,134111,111242,121142,121241,114212,124112,124211,411212,421112,421211,212141,214121,412121,111143,111341,131141,114113,114311,411113,411311,113141,114131,311141,411131,211412,211214,211232,23311120];
STOP       = 106 //BARS[STOP]==23311120 (manually added a zero at the end)

var fromType128 = {
    A: function(charCode) {
        if (charCode>=0 && charCode<32)
            return charCode+64;
        if (charCode>=32 && charCode<96)
            return charCode-32;
        return charCode;
    B: function(charCode) {
        if (charCode>=32 && charCode<128)
            return charCode-32;
        return charCode;
    C: function(charCode) {
        return charCode;

function code128(code, barcodeType) {
    if (arguments.length<2)
        barcodeType = code128Detect(code);
    if (barcodeType=='C' && code.length%2==1)
        code = '0'+code;
    var a = parseBarcode128(code,  barcodeType);
    return bar2html128(a.join('')) ;//+ '<label>' + code + '</label>';

function code128Detect(code) {
    if (/^[0-9]+$/.test(code)) return 'C';
    if (/[a-z]/.test(code)) return 'B';
    return 'A';

function parseBarcode128(barcode, barcodeType) {
    var bars = [];
    bars.add = function(nr) {
        var nrCode = BARS[nr];
        this.check = this.length==0 ? nr : this.check + nr*this.length;
        this.push( nrCode || format("UNDEFINED: %1->%2", nr, nrCode) );

    bars.add(START_BASE + barcodeType.charCodeAt(0));
    for(var i=0; i<barcode.length; i++)
        var code = barcodeType=='C' ? +barcode.substr(i++, 2) : barcode.charCodeAt(i);
        converted = fromType128[barcodeType](code);
        if (isNaN(converted) || converted<0 || converted>106)
            throw new Error(format("Unrecognized character (%1) at position %2 in code '%3'.", code, i, barcode));
        bars.add( converted );
    bars.push(BARS[bars.check % 103], BARS[STOP]);

    return bars;

function format(c){
    var d=arguments;
    var e= new RegExp("%([1-"+(arguments.length-1)+"])","g");
    return(c+"").replace(e,function(a,b){return d[b]})

function bar2html128(s) {
    for(var pos=0, sb=[]; pos<s.length; pos+=2)
        sb.push('<div class="bar' + s.charAt(pos) + ' space' + s.charAt(pos+1) + '"></div>');
    return sb.join('');