Fairly Positive

How the Barcodes Numbers Are Encoded and Decoded

In a previous post I noted that Telepen is the proprietary barcode format used in the Library at the University of Bristol. The Telepen symbology is publicly available and this post documents my understanding of the symbology.

The symbology has a number of key characteristics:

  • It represents the full ASCII character set
  • Characters take up the same amount of space
  • Wide to narrow bar ratio is 3:1
  • Four possible combinations of wide and narrow bars and spaces
  • Can be read as a binary sequence; uses 8-bit even-parity characters
  • Has a start character(_), stop character (z) and a check character

Telepen barcodes can represent numeric data (like the numbers used by the University) in a double-density mode. This means an ASCII character is used to represent a pair of numeric characters.


For each pair of numbers (15 11 07 59 64), we add 27 to get their ASCII representation (17-26 are reserved for the character series 0X to 9X), which leaves us with:

42 38 34 86 91

These should be prefixed by the start character ‘_’ (ascii value of 95) and postfixed by the check digit (90 in this case) and the stop character ‘z’ (ascii value 122), giving us:

95 42 38 34 86 91 90 122

The values can then be looked up in the symbology character set to create the barcode. Alternatively, for creating barcodes for testing, I used a barcode generator with the unencoded values:

telepen barcode for 1511075964

To decode the barcode you could analyse the image against the symbology character set. However, more interestingly, the barcode can be read as a bit stream. There are four possible patterns:

  • narrow bar + narrow space (1)
  • wide bar + narrow space (00)
  • wide bar + wide space (010)
  • narrow bar + wide space (01 or 10 - alternates within a byte)

We are dealing with 8-bit even-parity characters which are encoded with least significant bit first.

Looking at the barcode above, the first pattern is a narrow bar and a narrow space (1) giving us the pattern:


There are then four more narrow bar and narrow space patterns (1), giving us the following pattern:


The next pattern is then a wide bar + wide space (010), so we now have the following 8 bit pattern:


The decimal value for this pattern is 95, which represents ‘_’ in ascii which, in turn, is the start character for the barcode.

If we continued to look at the next set of bar and space patterns, we would get the following 8 bit pattern:


So, the decimal value for this pattern is 170. However, the most significant bit is set to 1 and this can be discounted to obtain the correct ascii value. The decoded bytes are even parity, so if the first 7 bits in the byte have an uneven number of 1s then the most significant bit will be set to 1 - this provides some simple error detection when decoding the barcodes.

We can use a bitwise operation to mask the most significant bit to obtain the ascii value:

int ascii_value = decoded_byte & 0x7F

In the case of 170 that would leave us with 42. If we deduct 27, that will leave 15, which are the first two digits if the barcode number 1511075964.

If we decoded all of the patterns we would have the following numeric values:

95 170 166 34 86 219 90 250

After we mask the most significant bit we are left with:

95 42 38 34 86 91 90 122

We can remove the start character ‘_’ (95) and the stop character ‘z’ (122) and that leaves us with the decoded numbers and the check digit:

42 38 34 86 91 90

To validate against the check digit obtain the sum of the numbers (excluding the check digit):

42 + 38 + 34 + 86 + 91 = 291

We then use modulus 127 to find the remainder:

291 % 127 = 37

Deduct the remainder from 127 to get the check digit:

127 - 37 = 90

So, the check digit matches! If we then subtracted 27 from each of the decoded numbers:

42 - 27 = 15, 38 - 27 = 11, 34 - 27 = 07, 86 - 27 = 59, 91 - 27 = 64

Thus, giving us 15 11 07 59 64. :-)

There are some exceptions to be noted.

  • The barcode numbers can end in X, and so the pair sequence 0X to 9x are represented by values to 17 to 26.
  • When working out the check digit, if the calculated value is 127 then the check digit is actually 0
  • There are other conditions when representing ascii values and not the double-density numeric mode - I won’t worry about those for the moment since I’m interested in only numeric values for this project.

In future posts, I’ll document how I’m attempting to write a decoder for the ZBar bar code reader so I can decode the telepen barcodes without using pencil and paper. :)