Código de barras com java

Daniel Gonçalves

Utilizando o padrão

Download do material relacionado ao tutorial



Códigos de Barras "Intercalado 2 de 5"

Quero começar este tutorial dizendo aos leitores que não tenho a menor intenção de "demonstrar código". Quero sim, abordar um assunto que é de extrema importância para os desenvolvedores de aplicações comerciais brasileiros. O enfoque deste artigo é a codificação em barras de sequências numéricas conhecida como "Interleaved 2 of 5".

Esse esquema de códigos de barras é utilizado (conforme regulamentação da FEBRABAN - Federação Brasileira dos Bancos), por todo o sistema bancário nacional para os boletos de cobrança bancária (já ouvi algumas pessoas chamado esses documentos de "bloquetos"..., enfim).

Aplicações comerciais que precisam emitir esses documentos completamente (desde a grade do boleto) usarão o esquema de códigos de barras Interleaved 2 of 5. Os bancos fornecem (vendem) boletos com a grade impressa (incluindo algumas outras informações como o nome do cedente, etc) onde as aplicações precisam imprimir apenas os dados, já que a grade e, inclusive o código de barras, são pré-impressos. No entanto, empresas que possuem uma movimentação muito grande (acima de 700 boletos/mês) descobrirão que imprimir o boleto completamente (a partir de uma folha em branco) em uma impressora laser, além de mais rápido, é significativamente mais barato.

Além disso, empresas que precisam emitir carnês de cobrança baseados em boleto (que possam ser pagos no sistema bancário ou por home-banking), podem emitir esses carnês.



Entendendo o "Interleaved 2 of 5"
Códigos de barras 2D são desenhados em uma sequência de barras verticais paralelas, finas e largas, alternadas entre brancas e escuras. Veja a imagem abaixo que mostra alguns esquemas de códigos de barras 2D e alguns 3D (os "3D" são chamados de códigos de barras apenas por vício -- na verdade ambos, 2D e 3D, são "esquemas de impressão de padrões para leitura por equipamentos eletrônicos")



O esquema "Interleaved 2 of 5" (I25) de códigos de barras permite que sejam codificados apenas sequências de dígitos numéricos. Alguns outros esquemas, como o Code39 ou Code128 (entre outros) permitem que sequências alfanuméricas e/ou símbolos sejam codificadas em barras.

Sendo assim, a especificação fornece dez "padrões" (patterns); um para cada dígito numérico (de 0 a 9). A figura 1 ilustra esses padrões.



A especificação de I25 diz que cada padrão (digito) deve ser intercalado (interleaved) com outro padrão. Assim, um par de dígitos pode ser representado por uma única unidade I25. A figura 2 mostra como um padrão deve ser intercalado com outro: o primeiro deve ser codificado nas barras escuras e o segundo nas barras claras.



Note que o termo "2 of 5" vem do fato de que em cada padrão, duas das barras são largas e, em uma única unidade I25, existem cinco barras escuras, assim como cinco barras claras (ou espaços). Ou seja: duas barras largas de um total de cinco barras por cada padrão.

A figura 2 mostra uma unidade I25 (ou símbolo). Ocorre que esse simbolo, se impresso, não poderá ser decodificado por um leitor de códigos de barras, por que ele está incompleto. Um código de barras I25 completo deve possuir uma "zona de silêncio" (quiet zone) antes e após as barras. Além disso, existem marcas (padrões) de início e término. A figura 3 mostra um código de barras I25 completo. Note também que, uma vez que um par de dígitos devem ser intercalados, então qualquer sequência a ser codificada deve possuir um número PAR de dígitos. Por exemplo, a sequência "11" é válida, mas "111" não é.





Uma solução orientada a objetos
O restante deste tutorial tenta desenvolver uma solução orientada a objetos para o problema dos códigos de barras. O que tentei fazer foi criar uma abordagem que permita alguma flexibilidade em sistemas onde o esquema de códigos de barras seja 2D mas as especificações possam mudar com o tempo ou dependa do que se deseja codificar.

O solução em si, que codifica uma sequência numérica usando os padrões I25, está escrita na classe concreta Interleaved2of5. Ocorre que essa classe estende uma classe abstrata chamada AbstractBarCode2D, que implementa a interface BarCode2D. A figura 4 mostra o diagrama de classes.

Para desenhar os padrões I25, a classe concreta Interleaved2of5 usa uma classe concreta chamada DefaultBarCode2DRenderer que, por sua vez, implementa a interface BarCode2DRenderer.



Você pode obter os fontes das classes e interfaces do diagrama, descompactando o arquivo barcode2d-fontes.jar (para não extender demais o artigo com código). Vou apresentar aqui apenas os trechos de código que "realizam o trabalho".

A classe Interleaved2of5 possui uma matriz de objetos String em uma constante contendo os padrões I25. Além de outras duas constantes contendo os padrões de marcação de início e término.

1 private static final String startPattern = "NNNN";
2 private static final String stopPattern  = "WNN";
3              
4 private static final String[] I25Pattern = "NNWWN""WNNNW",
5         "NWNNW""WWNNN",
6         "NNWNW""WNWNN",
7         "NWWNN""NNNWW",
8         "WNNWN""NWNWN" };


Vamos nos concentrar na implementação do método privado encodeValue().

01 private String encodeValue(String s) {
02     if ((s.length() == 0|| ((s.length() 2!= 0)) {
03         return "";
04     }
05 
06     char[] c = s.toCharArray();
07     StringBuffer encoded = new StringBuffer();
08                 
09     if (autoQuietZonesencoded.append("!");
10     if (autoStartStopMarksencoded.append(startPattern);
11 
12     for (int i = 0; i < c.length; i += 2) {
13         if (!Character.isDigit(c[i]) || !Character.isDigit(c[i + 1]))
14             return "";
15 
16         String d1 = I25Pattern[c[i48];
17         String d2 = I25Pattern[c[i + 148];
18 
19         for (int n = 0; n <= 4; n++) {
20             encoded.append(d1.charAt(n));
21             encoded.append(d2.charAt(n));
22         }
23     }
24                 
25     if (autoStartStopMarksencoded.append(stopPattern);
26     if (autoQuietZonesencoded.append("!");
27    
28     return encoded.toString();
29 }




Esse método recebe um argumento String que contém a sequência de digitos numéricos a serem codificados no esquema I25. Suponha "47". A primeira instrução if faz uma validação simples: verifica se o objeto String contém algo e se contém um número par de dígitos. Se o teste falhar, o método retornará uma String nula, o que significará que a sequência não pôde ser codificada.

Criamos então uma matriz de char contendo os caracteres do argumento (no nosso exemplo, c[0]='4' e c[1]='7').

Assim, temos praticamente todos os testes necessários realizados. O argumento está pronto para ser codificado no esquema I25.

O laço for deverá percorrer, de dois em dois, todos os caracteres obtidos do argumento. Obtemos a cada iteração o padrão I25 correspondente ao caracter apontado por i e também o padrão do caracter seguinte (a saber: "NNWNW" para o caracter '4' e "NNNWW" para '7'). Desse modo, o laço for mais interno deverá iterar 5 vezes (de 0 a 4). A cada iteração o n-ésimo caracter do primeiro padrão será adicionado ao objeto encoded e, em seguida, o n-ésimo caracter do segundo padrão. Dessa forma, teremos os padrões intercalados.

Ao final do laço for mais externo, neste exemplo, a chamada a encoded.toString() deverá retornar a sequência codificada ("NNNNWNNWWW"). Repare que a instrução if no laço for mais externo garante o restante da validação necessária, verificando se cada caracter da sequência é um dígito numérico.

Note que esse algoritimo de codificação poderia ser otimizado, mas achei que ficaria difícil de ler.

A classe que desenha as barras (DefaultBarCode2DRenderer) deve ser simples de ser compreendida.

O método render(), apenas recebe o componente em que deverá desenhar, a posição do canto superior esquerdo (x e y) de onde deve começar o desenho e a altura das barras. A especificação I25 diz que deve-se começar com uma barra escura; sendo assim, utilizei um flag bBar para alternar entre desenhar (com o método fillRect()) a barra ou simplesmente pular, orientado pelo padrão que é definido na classe Interleaved2of5. (Se você der uma olhada no fonte da classe AbstractBarCode2DRenderer verá que existem métodos render() que podem receber objetos Point ao invés de coordenadas separadas.). Segue o código:


01 public void render(Graphics g, int x, int y, int height) {
02     boolean  bBar = false;
03     int      iX   = x;
04     int      iW   = 0;
05 
06     g.setColor(Color.black);
07     for (int i = 0; i < pattern.length(); i++) {
08         switch (pattern.charAt(i)) {
09             case '!'/* zona de silencio */
10                 iW = quietZoneWidth;
11                 bBar = false;
12                 break;
13             case 'N'/* barra fina */
14                 iW = AbstractBarCode2DRenderer.NARROW_ELEMENT_WIDTH;
15                 break;
16             case 'W'/* barra larga */
17                 iW = AbstractBarCode2DRenderer.WIDE_ELEMENT_WIDTH;
18                 break;
19             defaultreturn;
20         }
21 
22         if (bBar) {
23             g.fillRect(iX, y, iW, y + height);
24         }
25        
26         iX += iW;
27         bBar = !bBar;
28     }
29 }


Note a utilização do sinal de exclamação para denotar a zona de silêncio (quiet zone).

No material disponível para download você encontrará o código fonte para todas as classes e interfaces (barcode2d-fontes.jar) e um outro arquivo JAR contendo essas mesmas classes compiladas (barcode2d.jar).

Inclui também um projeto simples (BarCodeSample.java) que utiliza as classes descritas.

Acrescente o arquivo barcode2d.jar ao CLASSPATH do seu ambiente e compile BarCodeSample.java (o código foi compilado e testado com a versão 1.4.1 do JDK).

Eis um screenshot de BarCodeSample:





Conclusão
Reforçando o que disse no início, não tenho a intenção de demonstrar código. O que vimos foi apenas "uma" das maneiras que "eu" usei para implementar uma solução que tivesse alguma flexibilidade (o que também não significa que usarei esse código exatamente como apresentei em meus próprios projetos, e nem espero que você o faça). Certamente, os desenvolvedores java mais experientes já imaginaram n maneiras diferentes e melhores de implementar uma solução. Mesmo assim, se você tiver entendido a especificação I25 e entendido como uma sequência pode ser codificada e desenhada em um componente, então terei atingido o meu objetivo.

Gostaria ainda de ver outros desenvolvedores, que tenham trabalhado com outras especificações (Code39, Code128, EAN-UPC, etc.), escreverem tutoriais mostrando a especificação e a sua visão de implementação. Acho que, além de interessante, iremos contruir uma importante fonte de referência em língua portuguesa, orientada a Java, para especificações de códigos de barras.

Referência
Para saber mais sobre especificações de códigos de barras, visite:

http://www.adams1.com/pub/russadam/barcode1.html (em inglês).


Copyright © 2002-2006 GUJ | Todas as marcas e marcas registradas que aparecem no GUJ são de propriedade de seus respectivos donos