Bar Codes
By Eric Juhel, 4D Developer
Technical Note 02-20
Technical Notes for Technical Notes for 02-05 May, 2002
Summary
Bar codes are part of an automatic identification technique that is broadly used in stock management, production management, shipment tracing, libraries, picture libraries, etc.
This technical note demonstrates a technique for for creating, displaying and printing code bars that match standard codes. Considering that bar codes will depend on the symbology used or the activity requirements, we will describe the four following codes: EAN 39 (European Article Numbering), EAN 128, UPC (Universal Product Coding), and the EAN 13.
The techinique used for all four examples is identical, the most difficult part being the creation of two 1 by 1 pixel pictures in the picture library.
Principle
Bar codes were designed in the early 1950's but were not actually implemented until the 1970's. The principle of bar coding is to alternate spaces and black bars in order to represent characters. Each subgroup represents one character of the encoded string. The character that matches a combination in a subgroup is described in the standard definition.
Some bar codes are numerical only (UPC. EAN), while others have have a fixed length (UPC-A consists of 12 numbers, UPC-E consists of 6 numbers and EAN-13 and EAN-8 consists of, respectively 13 and 8 numbers). Furthermore, some bar codes use alphanumerical characters and control characters (Code 93, Code 128 and code 39).
The principle could be summed up like this: Place black and white pixels side by side in a picture and vertically resize that picture so that it complies to the standard size.
Creating Pictures in the Library
To store two pictures in the picture library that are 1 by 1 pixel in size, you can just create two empty pictures in the picture library, assign them a size of 1 by 1 pixel, and, then assign a black background to one of them.
After this is done now, go through the definition of the bar code standards to implement thr proper functions.
Bar Code EAN 39
The code EAN 39 is an alphanumerical code that encodes 43 characters: numbers from 0 to 9, uppercase letters A through Z in addition to the symbols "_", ".", "*", "/", "%", "$", "+" and the space bar. It also includes a delimiter character that is found at the beginning and end of the message.
A "word" in EAN code breaks down as follows:
1- a space
2- the beginning character (delimiter)
3- the message contents
4- the ending character (delimiter)
5- a space
Each character is represented by nine elements: five bars for even ranks, and four spaces for odd ranks. Among these elements two bars and a space are wider than the other 6 elements. This structure type where 3 elements out of nine are wider gave the code its name (EAN 39).
In some cases, the word is to be completed by a control character. This character is the "modulo" 43 of the sum of all characters in the word. The values for the characters in EAN 39 is established as follows:
Character Value
| 0 | 0 |
| 1 | 1 |
| 2 | 2 |
| A | 0 |
......
| Z | 35 |
| dash | 36 |
| period | 37 |
| space | 38 |
| * | 39 |
| $ | 40 |
| / | 41 |
| + | 42 |
| % | 43 |
How do you compute the width of a symbol in EAN 39?
The length of a EAN 39 code can be determined by the following formula, where W is the width of the narrower elements and R is the multiplying factor that defines the wider elements.
Parameters
M1, M2 - Margins
Margins have to be at least 6 mm or 10 times the width of the narrowest number
N - Number of characters to code
16 x W - width of data characters, including the spaces between characters
31 x W - width of the beginning and ending characters, including spaces between the beginning character and the data
16 x W - Width of the optional control character
Printing Width = N (3WR +7W) + 6RN + 13 N + (3RN + 7N) + M1 +M2
Implementing the function EAN_EditBarcode_39
C_LONGINT($0;$error) `error
C_POINTER($1;$pict_BarCode) `pointer to barcode picture
C_TEXT($2) `text to encode
C_TEXT($string_entered;$vCode_Alpha) `string for retrieving
C_PICTURE($black_pixel) `Black pixel out of library
C_PICTURE($white_pixel) `White pixel out of library
C_LONGINT($modulo;$sum) `check sum
C_LONGINT($found) `array index
C_LONGINT($j;$i) `loop counters
C_PICTURE($picture;$pixel) `final picture and bar for final build
C_BOOLEAN($control) `True if control character needs to be there
ARRAY LONGINT($tl_code;0)
ARRAY TEXT($ta_code;0)
$pict_BarCode:=$1
$string_entered:=$2
If (Count parameters=3)
$control:=True
End if
If (Length($string_entered)>0) `If there is a string to code
GET PICTURE FROM LIBRARY(1000;$black_pixel)
GET PICTURE FROM LIBRARY(1001;$white_pixel)
If (Picture size($black_pixel)>0) & (Picture size($white_pixel)>0) `Do the pictures exist?
`loading choice list
LIST TO ARRAY("Characters_set_39";$ta_code;$tl_code)
If (Size of array($ta_code)>0) `Do the choice lists exist?
For ($i;1;Length($string_entered);1) `Looping through the length of the string
`researching the rank of the character using its ASCII code
$found:=Find in array($tl_code;Ascii(Uppercase($string_entered[[$i]])))
If ($found>0) `if the rank was retrieved
$code:=$ta_code{$found} `retrieving the 9 elements of the code
For ($j;1;9;1) `looping to interpret the 9 elements
If (($j/2)#($j\2)) `if the position is odd
$pixel:=$black_pixel `working with the black pixel
Else
$pixel:=$white_pixel `working with the white pixel
End if
If ($code[[$j]]="1") `if code 1 then it has to be thicker
$pixel:=$pixel*+3
End if
$picture:=$picture+$pixel `adding the current bar to the final picture
End for
End if
$picture:=$picture+$white_pixel `adding a space as character separator
If ($control) `if control character is needed
$sum:=$sum+($found-1) `there is a difference of 1 between the rank and actual value
End if
End for
If ($sum>0) `if control character is needed
$modulo:=($sum/43)-1 `substracting 1 to match the value in the choice list
$code:=$ta_code{$modulo} `retrieving the 9 elements of the code
For ($j;1;9;1) `loop to interpret the 9 elements
If (($j/2)#($j\2)) `if an odd position
$pixel:=$black_pixel `working with black pixel
Else
$pixel:=$white_pixel `working with white pixel
End if
If ($code[[$j]]="1") `if code 1 then increased thickness
$pixel:=$pixel*+3
End if
$picture:=$picture+$pixel `adding the current bar to the final picture
End for
End if ` If ($sum>0)
$picture:=$picture*/60 `vertical size for picture
$pict_BarCode->:=$picture `assigning the picture to the picture displayed
$error:=0 `no error
Else
`choice list is missing
ALERT("Resource error for the choice list...")
$error:=1
End if
Else
`picture in library error
ALERT("Resource error for the pictures...")
$error:=2
End if
Else
`error with the string entered
ALERT("The string to encode is empty...")
$error:=3
End if
$0:=$error
The code for each character is stored in a choice list named "Characters_Set_39". Using a choice list lets us define a reference number for each element in the list. We chose this reference to be the ASCII code of the encoded character.
The function proceeds in several steps:
1 - string to encode is recognized and characters are processed one by one
2 - identifying the character using its ASCII code
3 - researching the 9 elements of the code that matches the character
4 - selecting the black or white pixel based on the rank of the character (odd or even).
5 - taking into account the value of the element (0 or 1) to manage the width of the bar
6 - taking into account a possible control character
Bar Code EAN 128
The code EAN 128 is an alphanumerical code that encodes 103 of the ASCII characters. Each character encoding consists of playing on the distance between three spaces and bars which is the equivalent of putting 11 elements side by side. A bar or a space can be one, two, or three elements side by side. Bars are always in an even position and spaces are always in an odd position.
There are three ways for encoding a string in EAN 128, depending on the nature of the characters it includes. These ways are commonly referred to as code A, code B and code C.
Code A applies to strings with uppercase letters only.
Code B applies to strings with both uppercase and lowercase letters.
Code C applies to strings with pair-grouped numbers.
The coding also includes a beginning character which will depend on the encoding method used, and ending character that is common to the three encoding types. Finally, there will be a control character which is a the modulo 103 of the sum of all characters weighted by their position.
An example: the string "DIMENSION"
The presence of only uppercase characters leads to the use of code A, which makes the beginning character "A". This character is not taken into account in the computation of the control character.
Implementing the function EAN_Edit_Barcode_128
As with the previous encoding, the mapping of the characters is stored in a choice list named Characters_Set_128 which allows us to define a reference number for each item. The last four characters in the list represent the beginning and ending characters.
As with the previous code, the reference used is the ASCII value of the coded character. For the beginning and ending characters, that reference is arbitrary and not significant.
The function proceeds in several steps:
1 - identifying the code type to use (A, B or C)
2 - computing the control character
3 - updating the string to encode with the additional characters
4 - recognition of the string and processing each character, one at a time
5 - identifying characters using their ASCII code
6 - loading the element that matches the code
7 - taking the position (odd or even) into account to know which picture to use
8- weighting the pixel
Function EAN_Edit_Barcode_128
C_LONGINT($0;$error) `error
C_POINTER($1;$pict_BarCode) `pointer to bar code picture
C_TEXT($2) `text to encode
C_TEXT($String_entered;$vCode_Alpha) `retrieving string
C_PICTURE($Black_Pixel) `black pixel from picture library
C_PICTURE($Black_Pixel) `white pixel from picture library
C_LONGINT($modulo;$somme) `check sum
C_LONGINT($Found) `array indices
C_LONGINT($j;$i) `loop indices
ARRAY LONGINT($tl_code;0)
ARRAY TEXT($ta_code;0)
$modulo:=103
$somme:=$modulo
$pict_BarCode:=$1
$String_entered:=$2
If (Length($String_entered)>0)
GET PICTURE FROM LIBRARY(1000;$Black_Pixel)
GET PICTURE FROM LIBRARY(1001;$White_pixel)
If (Picture size($Black_Pixel)>0) & (Picture size($White_pixel)>0)
`loading patterns and references
`reference = ASCII code and the rank = value for referenced character
LIST TO ARRAY("Characters_set_128";$ta_code;$tl_code)
`a control character is mandatory for code 128.
`control character is modulo 103 of the weighted sum of all characters
If (Size of array($ta_code)>0)
`computing remainder
For ($i;1;Length($String_entered);1)
`ASCII code is used as reference in choice list
`the index $found matches the rank of the character $i
`in the pattern definition
$Found:=Find in array($tl_code;Ascii($String_entered[[$i]]))
If ($Found>0)
$somme:=$somme+(($Found-1)*$i) `weighting based on position
End if
End for
`retrieving remainder
$somme:=$somme\$modulo
$vCode_Alpha:=$String_entered
`creating the string to code
$String_entered:=Char(135)+$String_entered+Char($tl_code{$somme+1})+Char(138)
`creating the string
`calling patterns for each character (refer to references)
For ($i;1;Length($String_entered);1)
$Found:=Find in array($tl_code;Ascii($String_entered[[$i]]))
If ($Found>0)
$code:=$ta_code{$Found}
For ($j;1;Length($code);1)
If (($j/2)#($j\2)) ` if not in even position
$pixel:=$Black_Pixel `then it is a bar
Else
$pixel:=$White_pixel `otherwise it is a space
End if
$pixel:=$pixel*+(Num($code[[$j]])) `multiplying by the value [[$j]]
$pict_BarCode->:=$pict_BarCode->+$pixel
End for
End if
$pict_BarCode->:=$pict_BarCode->+$White_pixel `
End for
$String_entered:=$vCode_Alpha
$pict_BarCode->:=$pict_BarCode->*/30
$error:=0
Else
`choice list is missing
ALERT("Resource error for the choice list...")
$error:=1
End if
Else
`erreur images bibliothèque
ALERT("Resource error for the pictures...")
$error:=2
End if
Else
`error with the string entered
ALERT("The string to encode is empty...")
$error:=3
End if
$0:=$error
UPC and EAN Bar Codes
The EAN code is a numerical code designed for retail businesses. In the US, this code is the UPC code. The UPC and EAN codes share some common traits:
1- They address numerical data only.
2- They both have two bars and two spaces by character.
3- There are seven modules per character.
4- There are four level codes, and a bar or a space can be 1, 2, 3, or 4 modules wide.
5- They both have a mandatory control character.
EAN 13 symbols encodes 12 or 8 digits, the most common case being 12 digits. The UPC coding includes three subtypes: A, B, and E. We will exploring the type A, which encodes 12 characters.
Example:
UPC A Code
The 12 coded characters break down into three categories:
1- The first character is a system number that is supposed to describe a context
2- The 10 next characters are the actual encoded data
3- The last character is the control character
Separators indicate the beginning, middle and end of the encoded data.
The UPC coding imposes a peculiar mechanism when it comes to representing numbers: each number is coded on 7 binary elements where 1 is a black bar and 0 is a space (white bar). What is very peculiar with that is that the right and left sections are using inverted binary representation.
Number mapping with the UPC mapping:
Computing the control character
The steps to compute the control character are as follows:
1 - starting from the right assigning successively the odd and even positions to characters (the rightmost character is always the odd position character)
2 - adding the values of characters that have an odd position and multiplying the total by three
3 - adding the values of characters that have an even position
4 - adding the previous results
5 - subtracting the resulting value from the next multiple of 10
6- the control character is the result
For example:
The sum is 85 and the next multiple of 10 is therefore 90, which makes a control character of 5.
Implementing the function EAN_EditBarcode_UPC_A
The coding of the 9 numbers is stored in a choice list named "Characters_set_UPCa". This list only lists the mapping for the left section. The right section will be processed using the same list but using an inverted process.
Code EAN 13
The numbering of the system code is the main difference between the the EAN and the UPC A code.
In the EAN code, the system code, which is usually the country, is coded on two or three characters. Please refer to the appendixes for a list of those codes.
Another unique feature of the EAN 13 code is how the characters of the left section are encoded: the coding is different depending on the "status" (Odd or Even) of the character. Here, we use the term status rather than position since, according to the EAN standard, the position is not the only criterion to be considered when defining a character as Odd or Even. The parity status also takes into account the value of the first character. Please note that the second character is always considered as having an odd parity. A reference table determines the parity of the elements of the left section, based on the first character.
Table 1: Parity of the characters of the left section
In addition to the number encoding, the EAN 13 standard uses a second table that indicates the bar code based on the parity of the character and the section in which it is located.
Table 2: EAN 13 character encoding
The steps to compute the control code are identical to those of the UPC code:
1- Starting from the right, assign successively the odd and even position to the characters (the rightmost character is always to be considered as odd).
2- Add the values of the odd-positioned characters and mulitiply the total by 3.
3- Add the values of the even-positioned characters.
4- Add both totals.
5- Subtract the resultiing value from the next multiple of 10 to get the control character.
6- Determine the parity of the characters of the left section.
To determine the parity, we will be using a choice list named "Odd_Even" that reflects the mapping described in table 1. In the table, "0" represents the odd parity and "1" represents the even parity.
Implementing the function EAN_EditBarcode_13
C_LONGINT($0;$error) `error
C_POINTER($1;$pict_BarCode) `pointer to code bar picture
C_TEXT($2) `text to encode
C_TEXT($String_Entered) `retrieving string
C_PICTURE($black_pixel) `Black pixel out of library
C_PICTURE($black_pixel) `White pixel out of library
C_LONGINT($modulo;$sum) `checksum
C_LONGINT($found) `array index
C_LONGINT($j;$i) `loop counters
C_LONGINT($String_length) `llength of the string to encode
C_STRING(3;$Code_country) `system code (country of origin)
ARRAY LONGINT($tl_code;0)
ARRAY TEXT($ta_code;0)
ARRAY TEXT($ta_odd_even;0) `parity mask for left section
$pict_BarCode:=$1
$String_to_encode:=$2
$String_length:=Length($String_to_encode)
If ($String_length#12) & ($String_length#13)
ALERT("Entry error, "+Char(13)+"the length of the string should be 12 or 13 characters...")
$error:=-1
Else
End if
If ($error=0)
ARRAY STRING(7;$ta_CaracteSet;20)
LIST TO ARRAY("Characters_set_13";$ta_CaracteSet)
LIST TO ARRAY("odd_even";$ta_odd_even)
If ((Size of array($ta_CaracteSet)=0) | (Size of array($ta_odd_even)=0))
ALERT("Resource error in the choice lists...")
$error:=1
End if
If ($error=0)
ARRAY STRING(7;$ta_left_hand;10)
ARRAY STRING(7;$ta_left_even_hand;10)
ARRAY STRING(7;$ta_right_hand;10)
GET PICTURE FROM LIBRARY(1000;$black_pixel)
GET PICTURE FROM LIBRARY(1001;$white_pixel)
If (Picture size($black_pixel)=0) & (Picture size($white_pixel)=0)
ALERT("Picture resource error")
$error:=2
End if
End if
If ($error=0)
$image:=$white_pixel*+17
$image2:=$white_pixel*+17
For ($i;1;10)
$ta_left_hand{$i}:=$ta_CaracteSet{$i}
$ta_right_hand{$i}:=$ta_CaracteSet{$i+10}
$ta_left_even_hand{$i}:=$ta_CaracteSet{$i+20}
End for
$Code_country:=""
`left guard bars -> 101
$image:=$image+$black_pixel+$white_pixel+$black_pixel
$image2:=$image2+$black_pixel+$white_pixel+$black_pixel
`the country
Case of
: ($String_length=13)
$Code_country:=Substring($String_to_encode;1;3)
$Code_manufactury:=Substring($String_to_encode;4)
: ($String_length=12)
$Code_country:=Substring($String_to_encode;1;2)
$Code_manufactury:=Substring($String_to_encode;3)
: ($String_length<12)
$Code_country:=""
End case
`$odd_even -> determines the parity of the elements of the left section
`adding to identify its index in the list "Odd_Even"
`"Odd_Even" -> array $ta_odd_even
$odd_even:=Num($String_to_encode[[1]])+1
If ($Code_country#"")
`computing the control character on 12 characters
`as the last character is always considered with an odd parity
`inverting if the position is even -> it is considered odd
$check:=0
For ($i;12;1;-1)
If (($i/2)#($i\2)) `odd position
$check:=$check+(Num($String_to_encode[[$i]])) `even parity
Else
$check:=$check+(Num($String_to_encode[[$i]])*3) `odd parity
End if
End for
$check_S:=String($check)
$check:=10-Num(Substring($check_s;Length($check_s))) `10-extract the last number
$String_to_encode:=$String_to_encode+String($check)
$codealpha:=Substring($String_to_encode;2) `encoding after second character
$image:=$image | $image
$image2:=$image2 | $image2
`Left Hand bars
For ($i;1;6;1)
$digit:=Num($codealpha[[$i]])+1
`since we encode only after the second character => shifting $i by 1
If ($ta_odd_even{$odd_even}[[$i]]="0") `ODD
For ($rang;1;7;1)
If ($ta_left_hand{$digit}[[$rang]]="0")
$image:=$image+$white_pixel
Else
$image:=$image+$black_pixel
End if
$image2:=$image2+$white_pixel
$image:=$image | $image
$image2:=$image2 | $image2
End for
Else `-> EVEN PARITY
For ($rang;1;7;1)
If ($ta_left_even_hand{$digit}[[$rang]]="0")
$image:=$image+$white_pixel
Else
$image:=$image+$black_pixel
End if
$image2:=$image2+$white_pixel
$image:=$image | $image
$image2:=$image2 | $image2
End for
End if
End for
`center guard bars -> 01010
$image:=$image+$white_pixel+$black_pixel+$white_pixel+$black_pixel+$white_pixel
$image2:=$image2+$white_pixel+$black_pixel+$white_pixel+$black_pixel+$white_pixel
`Right Hand bars
For ($i;7;12;1)
$digit:=Num($codealpha[[$i]])+1
For ($rang;1;7;1)
If ($ta_right_hand{$digit}[[$rang]]="0")
$image:=$image+$white_pixel
Else
$image:=$image+$black_pixel
End if
$image2:=$image2+$white_pixel
End for
End for
`right guard bars -> 101
$image:=$image+$black_pixel+$white_pixel+$black_pixel
$image2:=$image2+$black_pixel+$white_pixel+$black_pixel
$image:=$image*/40
$image2:=$image2*/5
$image:=$image/$image2
$text_low:=Substring($Code_country;1;1)+(" "*5)+
Substring($String_to_encode;2;6)+(" "*5)+Substring($String_to_encode;8)+(" "*5)
$chart:=CT New offscreen area
$text:=CT Draw text ($chart;0;0;120;12;$text_low)
CT SET TEXT ATTRIBUTES ($chart;$text;CT Font number ("Helvetica");10;Plain ;
CT Index to color (16);0)
$picture_text:=CT Area to picture ($chart;$text)
CT DELETE OFFSCREEN AREA ($chart)
$picture_text:=($picture_text/41)
$image:=$image & $picture_text
$pict_BarCode->:=$image
End if
End if
End if
Particularity of the ISBN code (International Standard Book Number):
The ISBN code is destined to publishers and uses the EAN 13 coding. It corresponds to a 9-character string to which a control character is added. The code is divided into four distinct parts.
Examples:
The book 4D Expert has the following ISBN code: 2 - 212 -09163 - X which breaks down as follows:
- 2 represents the language or the country of origin
- 212 represents the publisher
- 09163 is a number assigned by the publisher
- X is the control character
To create the ISBN bar code, we used a method similar to the EAN 13 with a system code of 978. In other words, we coded the string: 978 2212 09163
Computing the control character for the ISBN code
The control character's computation is based on the sum of the 9 values, each weighted with it own coeficient. The coeficient value begins with 1 for the rightmost character and is incremented by one each time the character position is shifted to the left. The sum is then divided by 11 and the control character is the result of the substraction 11 - division remainder. When the result equals 10, the control character is the letter "X".
EAN 39 Mapping:
EAN 128 Mapping:
Main ountry codes for the ISBN encoding:
0 English )(UK,US,Australia,NZ,Canada,
1 English )South Africa,Zimbabwe)
2 French (France,Belgium,Canada,Switzerland)
3 German (Germany,Austria,Switzerland)
4 Japan
5 USSR
7 China
84 Spain
87 Denmark
88 Italian (Italy,Switzerland)
91 Sweden
92 International
Additional references:
http://www.barcodeisland.com/symbolgy.phtml
http://www.adams1.com/pub/russadam/barcode1.html