Trabajo con Códigos de Barras. Especificaciones EAN-13 y EAN-8


La especificación EAN en sus 2 versiones EAN 13 y EAN 8 es la especificación de código de barras europea por excelencia.
En las siguientes líneas veremos su fácil implementación en la programación, tanto para poder validar códigos correctos como
para poder dibujar códigos correctamente.

Tanto la especificacion EAN 13 (13 dígitos), como la EAN 8 (8 dígitos) se basan en el mismo sistema de codificación numérica.
El número es dividido en 2 zonas diferenciables, la parte izq. y la parte derecha. Dichas zonas son delimitadas por una barras de inicio de código, separación de código y fin de código. Estas barras son fácilmente diferenciables dentrop del código de barras pues su longitud es algo mayor.

Así tenemos un EAN 13: 8 429359 002008

   donde: 8 y 4 son dígitos de codificación, 29539 es la zona izq. de código t 00200 la zona drcha. y 8 el dígito de control.

Así tenemos un EAN 8: 8416 0089

   donde: 8 y 4 son dígitos de codificación, 8416 es la zona izq. de código t 008 la zona drcha. y 9 el dígito de control.

** Cada nº es codificado en una secuencia de 7 digitos binarios (1 o 0), dependiendo de la zona en que se encuentra.
En el tipo EAN 13, el primer dígito de codificación influye tambien en el método de codificación de la parte izq. del código.

Definiremos primero, las secuencias de 7 dígitos que codifican cada número en cada zona.
Denominaremos EAN_izqA y EAN_izqB a 2 matrices que contienen las secuencias de codificación de la parte izq. del código
Denominaremos EAN_dcha a la matriz que contiene las secuencias de codificación de la parte dcha. del código

Las barras de inicio, separación y fin de código se codifican siempre igual:

   Barras inicio de código -> codificadas como 101
   Barras separación central -> codificadas como 01010
   Barras fin de código -> codificadas como 101

const

  EAN_izqA : array[0..9] of PChar=('0001101','0011001','0010011','0111101','0100011','0110001','0101111','0111011','0110111','0001011');
  EAN_izqB : array[0..9] of PChar=('0100111','0110011','0011011','0100001','0011101','0111001','0000101','0010001','0001001','0010111');

  EAN_dcha : array[0..9] of PChar=('1110010','1100110','1101100','1000010','1011100','1001110','1010000','1000100','1001000','1110100');

El indice 0 de la matriz será la secuencia de codificación del nº 0, el índice 1 del número 1 y así sucesivamente..............

¿ Cuándo se codifica un nº con la secuencia de la matriz EAN_lA o con la EAN-lB ?
Como se comentó antes, esto solo se aplica a los códigos EAN-13 y dependerá del primer dígito de codificación. Para ello definimos la
matriz CodificaIzq.

  CodificaIzq : array[0..9] of PChar=('AAAAA','ABABB','ABBAB','ABBBA','BAABB','BBAAB','BBBAA','BABAB','BABBA','BBABA');

Aplicando esto, codificaremos el código EAN13 anterior para entender mejor el método: 8 429359 002008

   8 --> no se codifica, marca el tipo de codificación, o sea CodificaIzq[8]='BABBA';

Barras inicio
   4 --> se codifica siempre de la forma A, en este caso EAN_izqA[4]
   2 --> EANizqB[2]  \
   9 --> EANizqA[9]   \
   3 --> EANizqB[3]    |---------> codificados según manda CodificaIzq[8]
   5 --> EANizqB[5]   /
   9 --> EANizqA[9]  /
   Barras separación central
   0 --> EANdcha[0]
   0 --> EANdcha[0]
   2 --> EANdcha[2]
   0 --> EANdcha[0]
   0 --> EANdcha[0]
   8 --> EANdcha[8]
   Barras fin de código

Los códigos EAN 8 son más sencillos de codificar:
La parte izq. siempre con el método EAN_izqA y la derecha con EAN_dcha: 8416 0089

   Barras inicio de código
   8 --> EANizqA[8]
   4 --> EANizqA[4]
   1 --> EANizqA[1]
   6 --> EANizqA[6]
   Barras separación central
   0 --> EANdcha[0]
   0 --> EANdcha[0]
   8 --> EANdcha[8]
   9 --> EANdcha[9]
   Barras fin de código

El método de validación de dígito de control (el último nº en ambos casos) es sencillo:
Para EAN 13:
  Sumamos todos los dígitos impares -->
  Sumamos todos los dígitos pares y multiplicamos por 3 -->
  Sumamos ambas cifras -->
  El dígito de control será 10 menos las unidades del número anterior, si es 0 será 0 -->

Para EAN 8:
  Sumamos todos los dígitos impares y multiplicamos por 3 -->
  Sumamos todos los dígitos pares -->
  Sumamos ambas cifras -->
  El dígito de control será 10 menos las unidades del número anterior, si es 0 será 0 -->

Para el dibujado, los "1" equivaldrán a una línea de grosor entre 0.33 mm y los "0" a un especio del mismo grosor.
Se permite un margen error entre 0.23 y 0.33 mm en todos los grosores, pero eso no será necesario en nuestra implementación pues 0.33 mm equivalen al grosor de 1 pixel.

Creo que es bien sencillo de comprender, y una vez comprendidos estos 2 es sencillo conseguir información por la red para el resto de tipos, UPC, JAN,...

Para cualquier duda o aclaración no dudéis en poneros en contacto conmigo: [email protected]
Espero que os haya servido para ver que la dificaultad que entrañan es mínima.

 

A continuación veremos un pequeño ejemplo de todo esto implementado en Delphi:

unit EAN;

interface

uses
  Windows, SysUtils, Forms, Dialogs, StdCtrls, ExtCtrls, Classes, Controls;

const
  EAN_izqA : array[0..9] of PChar=('0001101','0011001','0010011','0111101','0100011','0110001','0101111','0111011','0110111','0001011');
  EAN_izqB : array[0..9] of PChar=('0100111','0110011','0011011','0100001','0011101','0111001','0000101','0010001','0001001','0010111');
  EAN_dcha : array[0..9] of PChar=('1110010','1100110','1101100','1000010','1011100','1001110','1010000','1000100','1001000','1110100');
  CodificaIzq : array[0..9] of PChar=('AAAAA','ABABB','ABBAB','ABBBA','BAABB','BBAAB','BBBAA','BABAB','BABBA','BBABA');

type
  TFCodigoBarras = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    Panel1: TPanel;
    PaintBox1: TImage;
    procedure Edit1KeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
  public
    { Public declarations }
    // procedimiento que codifica el número en un nº binario
    procedure Codifica(num : string);
    // procedimiento para dibujar el cód. de barras a partir del nº binario
    procedure Dibujar(matrix : string);
    // procedimiento para validar-corregir los códigos
    procedure EANCorrecto(var num : string);
  end;

var
  FCodigoBarras: TFCodigoBarras;

implementation

{$R *.DFM}

procedure TFCodigoBarras.EANCorrecto(var num : string);
var
  i,N : byte;
  sum : integer;
  flag : byte;
begin
  sum:=0;
  N:=Length(num)-1;
  for i:=1 to N do
  begin
    if (i mod 2)=0 then
    begin
      if N=12 then
        sum:=sum+StrToInt(num[i])*3
      else
        sum:=sum+StrToInt(num[i]);
    end
    else
    begin
      if N=12 then
        sum:=sum+StrToInt(num[i])
      else
        sum:=sum+StrToInt(num[i])*3;
    end;
  end;
  if sum>99 then
    Flag:=10-(sum mod 100)
  else
    Flag:=10-(sum mod 10);
  if Flag=10 then Flag:=0;
  if not(StrToInt(num[N+1])=flag) then
    num:=copy(num,1,length(num)-1)+IntToStr(Flag);
end;

procedure TFCodigoBarras.Codifica(num : string);
var
  matrix : string;
  i : integer;
begin
  num:=Edit1.Text;
  matrix:='';
  case Length(num) of
  13: begin
      EANCorrecto(num);
      Edit1.Text:=num;
      matrix:=matrix+'x0x'; // barra inicio
      matrix:=matrix+EAN_izqA[StrToInt(num[2])];
      for i:=3 to 7 do
        if CodificaIzq[StrToInt(num[1])][i-3]='A' then
          matrix:=matrix+EAN_izqA[StrToInt(num[i])]
        else
          matrix:=matrix+EAN_izqB[StrToInt(num[i])];
      matrix:=matrix+'0x0x0'; // barra central
      matrix:=matrix+EAN_dcha[StrToInt(num[8])];
      matrix:=matrix+EAN_dcha[StrToInt(num[9])];
      matrix:=matrix+EAN_dcha[StrToInt(num[10])];
      matrix:=matrix+EAN_dcha[StrToInt(num[11])];
      matrix:=matrix+EAN_dcha[StrToInt(num[12])];
      matrix:=matrix+EAN_dcha[StrToInt(num[13])];
      matrix:=matrix+'x0x'; // barra final
      Dibujar(Matrix);
    end;
  8: begin
      EANCorrecto(num);
      Edit1.Text:=num;
      matrix:=matrix+'x0x';
      matrix:=matrix+EAN_izqA[StrToInt(num[1])];
      matrix:=matrix+EAN_izqA[StrToInt(num[2])];
      matrix:=matrix+EAN_izqA[StrToInt(num[3])];
      matrix:=matrix+EAN_izqA[StrToInt(num[4])];
      matrix:=matrix+'0x0x0';
      matrix:=matrix+EAN_dcha[StrToInt(num[5])];
      matrix:=matrix+EAN_dcha[StrToInt(num[6])];
      matrix:=matrix+EAN_dcha[StrToInt(num[7])];
      matrix:=matrix+EAN_dcha[StrToInt(num[8])];
      matrix:=matrix+'x0x';
      Dibujar(Matrix);
    end
  else
    ShowMessage('LONGITUD DE CODIGO NO VALIDA');
  end;

end;

procedure TFCodigoBarras.Dibujar(matrix : string);
var
  i : integer;
begin
  PaintBox1.Canvas.Brush.Color:=clWhite;
  PaintBox1.Canvas.FillRect(Rect(0,0,PaintBox1.Width,PaintBox1.Height));
  PaintBox1.Canvas.Pen.Color:=clBlack;

  for i:=1 to Length(Matrix) do
    if matrix[i]='1' then
      PaintBox1.Canvas.PolyLine([Point(10+i,10),Point(10+i,50)])
    else
    if matrix[i]='x' then
      PaintBox1.Canvas.PolyLine([Point(10+i,10),Point(10+i,55)]);

  if Length(Edit1.Text)=13 then
  begin
      PaintBox1.Canvas.TextOut(3,50,Edit1.Text[1]);
      PaintBox1.Canvas.TextOut(17,50,copy(Edit1.Text,2,6));
      PaintBox1.Canvas.TextOut(63,50,copy(Edit1.Text,8,6));
  end
  else
  if Length(Edit1.Text)=8 then
  begin
      PaintBox1.Canvas.TextOut(16,50,copy(Edit1.Text,1,4));
      PaintBox1.Canvas.TextOut(48,50,copy(Edit1.Text,5,4));
  end;

  PaintBox1.Picture.SaveToFile('c:\windows\escritorio\BarCode\'+Edit1.Text+'.bmp');
end;

procedure TFCodigoBarras.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key=#13 then
  begin
    Codifica(Edit1.Text);
    Edit1.SelStart:=0;
    Edit1.SelLength:=Length(Edit1.Text);
  end;
end;

end.


José Rubén
[email protected]