Bar Code 39 Implementation

Code 39 can include upper case letters, numbers and limited punctuation. When planning or designing forms that will have bar codes on the, allow for the following space.

Minimum bar code space when document will be scanned

# characters of data
don't count start/stop characters
length (in) height (in)
including quiet zone
4 1.3 0.3
5 1.5 0.4
6 1.7 0.4
7 1.9 0.5
8 2.1 0.5
10 2.5 0.6
12 2.9 0.7
14 3.3 0.8
16 3.7 0.9
18 4.1 1.0
20 4.5 1.1

These sizes will work well for documents that will be scanned at 200 dpi (normal setting for business document capture) or faxed. Faxes with bar codes should not be sent in the older low-resolution fax setting, which was 100 dpi.
The bar code elements that lead to these recommendations are:
narrow bar width: .011 in (2.2 pixels at 200 dpi, or 1.1 pixels at 100 dpi which is barely hanging on for a low-res fax)
intercharacter gap: .053 in (the bar code 39 spec)
wide to narrow bar ratio: 2.5 (2.2 is acceptable but only makes the code slightly smaller)
width to height ratio: 0.26, which tolerates 15° of skew when scanning. (a ratio of 0.15 is accpetable, but requires scanning to less than 9° of skew)
quiet zone: .11 in, minimum white space on left and right of the bars. There is no space required above or below the bars.


Check Your Bar Code 39

After you print your bar code, check that it conforms to the Bar Code 3 of 9 specification. Here's how a visual inspection can be easily accomplished:

  1. Bar code 39 must have a quiet zone before the bars start and after the bars end. Check that both are at least 3/16" for optimal reading.
  2. Bar code 39 must have a start character. Check that your bar code starts with: Narrow bar, wide space, Narrow bar, narrow space, Wide bar, narrow space, Wide bar, narrow space, Narrow bar. (NwNnWnWnN)
  3. Every character is encoded in five bars and four spaces. A narrow space separates every character. Look up each character in your data, and check that the bars and spaces match the Narrow/Wide sequence in the adjacent table. Table Abbreviation: upper case for the bar (black) width and lower case for the space (white) width.
  4. Bar code 39 must end with the stop character, which is the same as the start character. Check that your bar code ends with: Narrow bar, wide space, Narrow bar, narrow space, Wide bar, narrow space, Wide bar, narrow space, Narrow bar. (NwNnWnWnN)
  5. There is no space required above or below the bars.
Bar Code 39
CharacterPattern CharacterPattern
start/stop NwNnWnWnN A WnNnNwNnW
- NwNnNnWnW B NnWnNwNnW
$ NwNwNwNnN C WnWnNwNnN
% NnNwNwNwN D NnNnWwNnW
(space) NwWnNnWnN E WnNnWwNnN
. WwNnNnWnN F NnWnWwNnN
/ NwNwNnNwN G NnNnNwWnW
+ NwNnNwNwN H WnNnNwWnN
0 NnNwWnWnN I NnWnNwWnN
1 WnNwNnNnW J NnNnWwWnN
2 NnWwNnNnW K WnNnNnNwW
3 WnWwNnNnN L NnWnNnNwW
4 NnNwWnNnW M WnWnNnNwN
5 WnNwWnNnN N NnNnWnNwW
6 NnWwWnNnN O WnNnWnNwN
7 NnNwNnWnW P NnWnWnNwN
8 WnNwNnWnN Q NnNnNnWwW
9 NnWwNnWnN R WnNnNnWwN
S NnWnNnWwN
T NnNnWnWwN
U WwNnNnNnW
V NwWnNnNnW
W WwWnNnNnN
X NwNnWnNnW
Y WwNnWnNnN
Z NwWnWnNnN


Character Value Character Value
0 0 M 22
1 1 N 23
2 2 O 24
3 3 P 25
4 4 Q 26
5 5 R 27
6 6 S 28
7 7 T 29
8 8 U 30
9 9 V 31
A 10 W 32
B 11 X 33
C 12 Y 34
D 13 Z 35
E 14 - 36
F 15 . 37
G 16 space 38
H 17 $ 39
I 18 / 40
J 19 + 41
K 20 % 42
L 21    

Code 39 mod 43 Checksum

Acordex recommends the use of a checksum for all bar codes. Even though the character codes themselves are self-checking, damaged images (such as white or black vertical stipes can occur on faxes) can create valid but incorrect bar codes. The code with check digit is referred to as Code 39 mod 43.

Here is how to do the checksum calculation:

  1. Take the value (0 through 42) of each character in the barcode excluding start and stop codes.
  2. Sum the values.
  3. Divide the result by 43.
  4. The remainder is the value of the checksum character to be appended.
Note: If you print a human readable string above the bar code, the checksum character should NOT be printed in the human readable string.


Example checksum implementations

Example Python implementation
def append_c39_checksum(st):
    charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"
    return st + charset[ sum(charset.index(c) for c in st) % 43 ]

Const charSet As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"

Function Mod43(C39 As String) As String
  For i = 1 To Len(C39)
    total = InStr(charSet, Mid(C39, i, 1)) - 1 + total
  Next i
  Mod43 = C39 & Mid(charSet, (total Mod 43 + 1), 1)

End Function
' compute checksum for Code 39, translated by antiguide
' paste the code in a .vbs file (MsWindos)
dim charSet 
charset= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"
dim rejected ' string
rejected=""
dim total ' integer

total=0
dim c39
c39=inputbox ("Code to generate: ")
  For i = 1 To Len(C39)
    dim rank
    rank= InStr(charSet, Mid(C39, i, 1)) - 1
    if (rank >= 0) then     
       total =  total +rank
    else

       rejected=rejected & Mid(C39, i, 1)
  end if
  Next 
if rejected = "" then 

msgbox  c39 & Mid(charSet, (total Mod 43 + 1), 1)
else
  msgbox  "wrong characters: " & rejected
end if

public static final String charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";

public static char getChecksum(String barCode) throws Exception {

  int total = 0;
  CharacterIterator it = new StringCharacterIterator(barCode);

  for (char ch = it.current(); ch != CharacterIterator.DONE; ch = it.next()) {

    int charValue = charSet.indexOf(ch);
    if (charValue == -1) {

      // Invalid character.
      throw new Exception("Input String '" +barCode+ "' contains characters that are invalid in a Code39 barcode.");

    }
    total += charValue;
  }
  int checksum = total % 43;

  return charSet.charAt(checksum);
}
public string ValidateMod43(string barcode)

{
   int subtotal = 0;
   const string charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"; 
 
   foreach (char c in barcode)

      subtotal += charSet.IndexOf(c);
 
   return charSet[subtotal%43].ToString();

}
use List::Util qw(sum);

use feature 'state';
 
sub checksum {
    state $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%';

    return substr( $charset, sum( 0, map { index $charset, $_ } split //, shift ) % 43, 1 );

}
function checksum( $string )

{
	$checksum = 0;
	$length   = strlen( $string );

	$charset  = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%';
 
	for( $i=0; $i < $length; ++$i )

	{
		$checksum += strpos( $charset, $string[$i] );

	}
 
	return $charset[ $checksum % 43 ];

}
proc checksum string {

	set checksum 0
	set charset "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%"
	foreach char [split $string ""] {

		if {-1 == [set value [string first $char $charset]]} {
			return -code error "Wrong Code39 character:$char"
		}

		incr checksum $value
	}
	return [string index $charset [expr {$checksum % 43}]]

}

The above examples are excerpted from Wikipedia.

Try a checksum calculation.
Input string:
Mod 43 checksum: