terça-feira, janeiro 04, 2005

Criando código de Barras

Criar código de barras é uma tarefa que exige entender a regra da criação das barras e seus significados.

Para um dos projetos em desenvolvimento, precisei da geração de códigos de barras para o formato Interleaved 2 of 5 (intercalado 2 de 5).

Como JAVA tem um universo aberto, fui ao encontro de API que fazem este serviço de geração do código de barras. Encontrei estes:
http://jbars.sourceforge.net/ - Open Source
http://java4less.com/barcodes_e.htm - Parece ser pago

Mas destes dois, nenhum foi possível gerar e validar na agência bancária... Nem no caixa eletronico...

Então como precisava gerar o código correto para o Interleaved 2 of 5, peguei a especificação para geração do código de barras e fiz minha implementação...

Ficou em duas classes, pensando numa futura evolução para outros modelos... Mas isto se precisar.

As classes ficaram assim:
Barcode.java
/*
* Created on 28/12/2004
*
*/
package br.com.gadew.jbarcode;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

/**
* @author Adriano
*
*/
public abstract class Barcode {

public int ALTURA;
public int LARGURA;

public int MARGEM_SUPERIOR;
public int MARGEM_INFERIOR;
public int MARGEM_ESQUERDA;
public int MARGEM_DIREITA;

public Color COR_FUNDO;
public Color COR_BARRAS;

protected String codigo;

public BufferedImage render() {

BufferedImage bufferedImage = new BufferedImage(LARGURA,
ALTURA, java.awt.image.BufferedImage.TYPE_INT_RGB);

Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();

validarCodigo();

graphics.setPaint(COR_FUNDO);
graphics.fillRect(0, 0, LARGURA, ALTURA);

graphics.setColor(COR_BARRAS);
int posicaoFinal = renderize(graphics);

graphics.setPaint(COR_FUNDO);
graphics.fillRect(posicaoFinal, 0, MARGEM_DIREITA, ALTURA);

if (posicaoFinal > LARGURA) {
System.err.println("Barras ultrapassam o tamanho da figura em "
+ (posicaoFinal - LARGURA));
}

return bufferedImage;
}

protected abstract int renderize(Graphics2D graphics);
protected abstract void validarCodigo();
}


e o arquivo
Interleaved2of5.java
/*
* Created on 28/12/2004
*
*/
package br.com.gadew.jbarcode;

import java.awt.Graphics2D;
import java.awt.Rectangle;

/**
* @author Adriano
*
*/
public class Interleaved2of5 extends Barcode {

private static final int LARGURA_N = 1;
private static final int LARGURA_W = 3;

private static final char[] padraoInicial =
new char[] { 'n', 'n', 'n', 'n' };
private static final char[] padraoFinal =
new char[] { 'w', 'n', 'n' };

private static char[][] padrao =
new char[][] { { 'n', 'n', 'w', 'w', 'n' },// 0
{ 'w', 'n', 'n', 'n', 'w' },// 1
{ 'n', 'w', 'n', 'n', 'w' },// 2
{ 'w', 'w', 'n', 'n', 'n' },// 3
{ 'n', 'n', 'w', 'n', 'w' },// 4
{ 'w', 'n', 'w', 'n', 'n' },// 5
{ 'n', 'w', 'w', 'n', 'n' },// 6
{ 'n', 'n', 'n', 'w', 'w' },// 7
{ 'w', 'n', 'n', 'w', 'n' },// 8
{ 'n', 'w', 'n', 'w', 'n' },// 9
};

public Interleaved2of5(String codigo) {
super.codigo = codigo;
}

private String padrao() {

StringBuffer buffer = new StringBuffer();
buffer.append(padraoInicial);

for (int i = 0; i < codigo.length(); i += 2) {

String primeiro = codigo.substring(i, i + 1);
String segundo = codigo.substring(i + 1, i + 2);

char[] valorPrimeiro = padrao[Integer.parseInt(primeiro)];
char[] valorSegundo = padrao[Integer.parseInt(segundo)];

buffer.append(concatena(valorPrimeiro, valorSegundo));
}

buffer.append(padraoFinal);
return buffer.toString();
}

private StringBuffer concatena(char[] valorPrimeiro,
char[] valorSegundo) {

StringBuffer buffer = new StringBuffer();

for (int i = 0; i < valorSegundo.length; i++) {
buffer.append(valorPrimeiro[i]).append(valorSegundo[i]);
}

return buffer;
}

/*
* (non-Javadoc)
*
* @see br.com.gadew.jbarcode.Barcode#validarCodigo()
*/
protected void validarCodigo() {
if (codigo == null) {
throw new IllegalArgumentException("Valor do código é null");
}
String somenteNumeros = codigo.replaceAll("\\D", "");
if (somenteNumeros.length() % 2 != 0) {
throw new IllegalArgumentException("O tamanho do código deve "
+ "consistir de tamanho par e somente números.");
}
}

/*
* (non-Javadoc)
*
* @see br.com.gadew.jbarcode.Barcode#renderize(java.awt.Graphics2D)
*/
protected int renderize(Graphics2D graphics) {

int posicao = MARGEM_ESQUERDA;
boolean imprimir = true;

int alturaBarra = ALTURA - (MARGEM_SUPERIOR + MARGEM_INFERIOR);

String modelo = padrao();

for (int i = 0; i < modelo.length(); i++) {
char tipo = modelo.charAt(i);
posicao = imprimirQuadrado(tipo,
graphics,
posicao,
alturaBarra,
imprimir);
imprimir = !imprimir;
}

return posicao;
}

/**
* @param tipo
* @param graphics
* @param posicao
* @param alturaBarra
* @param imprimir
* @return
*/
private int imprimirQuadrado(char tipo,
Graphics2D graphics,
int posicao,
int alturaBarra,
boolean imprimir) {

Rectangle quadrado = new Rectangle();
quadrado.setLocation(posicao, MARGEM_SUPERIOR);
switch (tipo) {
case 'n':
quadrado.setSize(LARGURA_N, alturaBarra);
posicao += LARGURA_N;
break;

case 'w':
quadrado.setSize(LARGURA_W, alturaBarra);
posicao += LARGURA_W;
break;
}
if (imprimir) {
graphics.fill(quadrado);
}
return posicao;
}
}


E para a utilização do código é simples, eu fiz para utilizar em um Servlet:
Barcode bar = new Interleaved2of5(request.getParameter("codigo"));

bar.ALTURA = 50;
bar.LARGURA = 450;
bar.COR_FUNDO = Color.WHITE;
bar.COR_BARRAS = Color.BLACK;
bar.MARGEM_ESQUERDA = 15;
bar.MARGEM_DIREITA = 15;

BufferedImage BarImage = bar.render();

OutputStream os = response.getOutputStream();

JPEGImageEncoder coder = JPEGCodec.createJPEGEncoder(os);
coder.encode(BarImage);

os.flush();
os.close();


Agora só falta implementar os demais códigos de barras.!

Gráficos JAVA no Linux sem X11

Uma coisa que nos colocam em problemas é a portabilidade...
Como diz um amigo "A Interoperabilidade".

Com uma aplicação pronta no ambiente windows, precisava ser apenas colocado em um sistema unix (Solaris)
Algumas coisas que sempre esquecemos é a bendita barra "\"... É importante utilizar a barra "/" sempre que se referenciar a estrutura de diretórios...

Mas este é apenas um detalhe...
A utilização de gráficos no Linux envolve a utilização do servidor X11 rodando.
Então se você for criar relatórios no Jasper, utilizar o JFreeChart ou gerar uma imagem... é preciso o X11 inicializado.

Mas muitas vezes o sistema operacional no servidor é instalado e dificilmente é inicializado o X11, pois seu acesso é feito por terminais...

Como o JAVA utiliza o pacote AWT e este que utiliza recursos do sistema para montar os gráficos, fica complicado sua aplicação ser independente do X11.

Mas a SUN pensou neste problema e para isto colocou um parametro de inicialização nas suas aplicações. o parametro é:

-Djava.awt.headless=true

Assim você pode colocar este parametro na inicilização de suas aplicações para rodar sem a necessidade do X11
java -Djava.awt.headless=true MyJavaApplication

Como o meu caso é uma aplicação web rodando no Tomcat, foi necessário colocar a linha abaixo no começo do arquivo catalina.sh e executar o startup.sh
CATALINA_OPTS="-Djava.awt.headless=true"

Estas coisas sempre nos segura até mais tarde na empresa... Imagine que isto me segurou até os 46 do segundo tempo antes do ano novo.