Lire de l'Interleaved 2 of 5 sans dispositif électronique

Date de publication : — dernière modification :

Interleaved 2 of 5 (souvent abrévié en ITF) est un format de code barre utilisé dans de nombreuses applications, notamment pour identifier des élèves à la cantine. Il ne permet que d'encoder des chiffres, même si certaines variantes permettent d'encoder des caractères supplémentaires.

J'avais besoin de récolter une quantité conséquente de codes barres ITF, sans avoir à recourir à un dispositif électronique. J'ai donc inventé un moyen facile et efficace pour les recopier sans avoir un smartphone ou un appareil photo à portée de main.

Comment est formé l'ITF ?

Les codes barres ITF sont composés de 4 informations :

  • Des barres blanches courtes
  • Des barres blanches longues
  • Des barres noires courtes
  • Des barres noires longues

Chaque code barre ITF est doté d'un code start au début et d'un code stop à la fin. Ces codes permettent essentiellement de connaître le sens de défilement du code barre, et potentiellement aussi la vitesse de défilement.

Le code start est formé la succession d'une barre noire courte, une barre blanche courte, une barre noire courte, et une barre blanche courte. Le code stop est formé d'une barre noire longue, d'une barre blanche courte, puis d'une barre noire courte.

Les chiffres sont encodés entre le start et le stop. Ils sont encodés deux par deux, en utilisant 5 barres noires et 5 barres blanches ; le premier chiffre sera encodé avec les barres noires, le second avec les barres blanches.

Les chiffres sont encodés par la longueur des barres, par exemple pour le chiffre 5, on aura : Long-Court-Long-Court-Court. Voici le tableau pour pouvoir décoder tous les chiffres :

Chiffre Code
1 Long-Court-Court-Court-Long
2 Court-Long-Court-Court-Long
3 Long-Long-Court-Court-Court
4 Court-Court-Long-Court-Long
5 Long-Court-Long-Court-Court
6 Court-Long-Long-Court-Court
7 Court-Court-Court-Long-Long
8 Long-Court-Court-Long-Court
9 Court-Long-Court-Long-Court
0 Court-Court-Long-Long-Court

On note qu'on a pour chaque chiffre deux barres longues sur un total de 5 barres, d'où le "2 of 5", et que les chiffres sont encodés alternativement, d'où le "interleaved". Mainenant vous avez compris pourquoi on dit "2 of 5 interleaved".

Simplifier l'écriture du code barre sur du papier

Actuellement, pour décoder de l'ITF, nous devons écrire les 4 informations sur du papier (barre courte blanche, barre longue blanche, etc.). J'ai constaté qu'on pouvait ne pas écrire une des informations : par exemple, après une barre noire, qu'elle soit courte ou longue, il y aura forcément une barre blanche courte ou longue. Deux barres blanches ne pouvant être collées, on peut oublier l'information de la barre blanche courte.

Du coup, je suggère cette notation :

  • Une barre noire courte sera un 1
  • Une barre noire longue sera un 2
  • Une barre blanche longue sera un 0
  • On ne note pas les barres blanches

On omet aussi d'écrire les codes start et stop, par contre il faut penser à lire le code barre dans le bon sens (du start vers le stop).

Donc le code barre suivant :

Un code barre représentant le nombre 1234

S'écrira : 2 1 0 1 1 2 0 2 2 1 0 1 1 0.

Le décodage

Le décodage va se dérouler en deux passes. La première passe va consister à "décompresser" notre notation simplifiée : on va surtout ajouter les barres blanches courtes. La seconde passe consiste à lire le code barre, de la manière indiquée plus haut dans cet article.

J'appuierais mes propos sur un petit algorithme codé en Ruby. Voici le bootstrap :

if (ARGV[0].nil?)
  puts "Usage : ./itf_decoder.rb [your barcode here]"
  exit
end

simplified_barcode = ARGV[0].split("")

Nous allons ici faire la première passe. Je code en Ruby, donc j'utilise des symboles. On pourrait très bien faire une enum en C, ou utiliser des caractères alphanumériques ...

real_barcode = []
simplified_barcode.each_with_index do |value,index|
  if value == '0'
    real_barcode << :wide_white
  elsif value == '1'
    real_barcode << :narrow_black
    if simplified_barcode[index+1] != '0' # On ajoute un zéro si besoin
      real_barcode << :narrow_white
    end
  elsif value == '2'
    real_barcode << :wide_black
    if simplified_barcode[index+1] != '0' # On ajoute un zéro si besoin
      real_barcode << :narrow_white
    end
  end
end

Maintenant nous allons faire la seconde passe. On lit par bloc de 10 informations, on sépare le blanc du noir et on compare les informations blanches et les informations noires à la table des valeurs :

# Table des valeurs
values_table = {
  [:narrow,:narrow,:wide,:wide,:narrow] => 0,
  [:wide,:narrow,:narrow,:narrow,:wide] => 1,
  [:narrow,:wide,:narrow,:narrow,:wide] => 2,
  [:wide,:wide,:narrow,:narrow,:narrow] => 3,
  [:narrow,:narrow,:wide,:narrow,:wide] => 4,
  [:wide,:narrow,:wide,:narrow,:narrow] => 5,
  [:narrow,:wide,:wide,:narrow,:narrow] => 6,
  [:narrow,:narrow,:narrow,:wide,:wide] => 7,
  [:wide,:narrow,:narrow,:wide,:narrow] => 8,
  [:narrow,:wide,:narrow,:wide,:narrow] => 9
}

# Deux fonctions pour séparer les infos blanches & noires vu qu'on lit le code barre par chunk de 10 barres
def filterWhite(input_data)
  output = []

  for item in input_data
    if (item == :narrow_white)
      output << :narrow
    elsif (item == :wide_white)
      output << :wide
    end
  end

  return output
end

def filterBlack(input_data)
  output = []

  for item in input_data
    if (item == :narrow_black)
      output << :narrow
    elsif (item == :wide_black)
      output << :wide
    end
  end

  return output
end

output_code = []

# On lit le code barre par chunk de 10 barres, on sépare le blanc du noir, et on trouve la valeur du code
while real_barcode.count != 0
  # On lit les 10 barres
  current_portion = real_barcode.shift(10)

  # On stocke uniquement les barres noires dans une variable
  black_codes = filterBlack(current_portion)

  # On fait de même pour les barres blanches
  white_codes = filterWhite(current_portion)

  output_code << values_table[black_codes]
  output_code << values_table[white_codes]
end

# On affiche la valeur du code barre
puts output_code.join("")

Le code source complet du script est disponible ici.

Maintenant vous savez comment lire un code barre sans dispositif électronique lors de la lecture, avec une méthode facile, et surtout comment obtenir la valeur du code barre à partir de la lecture.