ZXing: Como ler códigos de barras em Java

Veja neste artigo como usar a biblioteca Zxing em Java para fazer leitura de código de barras em imagens.

Neste artigo veremos o uso prático de uma biblioteca chamada de Zxing, que tem por objetivo trabalhar com código de barras 1D e 2D. Mas, primeiro, devemos entender o que é um código de barras e como ele funciona.

O Código de Barras é uma representação gráfica de dados numéricos ou alfanuméricos, estes estão presentes com muita frequência no nosso dia a dia e tem a representação como na Figura 1.

Figura 1. Ilustração de um Código de Barras

A decodificação ou leitura da Figura 1 é realizada da seguinte forma: um scanner (leitor de código de barras) emite um raio vermelho que percorre todas as barras e onde a barra for escura, a luz é absorvida, e onde for branca (espaços em branco) a luz é refletida para o scanner e no computador é feita a conversão para números ou letras.

Existem diversos formatos de código de barras, por exemplo: Código 39, EAN-13, GS1-128, DUN-14 e etc. Cada um destes tem sua especificidade, por exemplo: o código 39 ou Code 39 possibilita a codificação de letras e números e mais alguns símbolos especiais.

Obviamente que o conceito apresentado aqui foi apenas introdutório em relação ao funcionamento do código de barras para que você leitor possa continuar lendo o restante do artigo com mais confiança no assunto tratado. Existem muitas mais características em relação a cada formatação e como elas são feitas, mas não é foco do nosso artigo apresentar tais conceitos e sim o funcionamento na prática da biblioteca Zxing.

Zxing

Nem sempre temos o leitor de código de barras (scanner) em mãos ou temos situações em que precisamos dispensar o seu uso e é aqui que entra a API Zxing. Para entender melhor, vamos a um cenário real: “O seu sistema deve possuir um módulo onde o usuário fará o upload de contratos assinados pelo seu cliente (imagens) e o sistema deve associar automaticamente estas imagens com o cliente correto sem nenhuma intervenção humana. Na capa do contrato, que pode possuir N folhas, há um código de barras que identifica a numeração do contrato (a numeração do contrato possui o código do cliente em sua composição)."

Sendo assim, nós podemos usar o Zxing para “ler” a capa do contrato e extrair a numeração contida no código de barras e após extração da numeração nós podemos buscar o cliente correspondente. Dado o cenário exposto, nós podemos perceber que o Zxing é muito útil quando precisamos ler as informações contidas no código de barras e não temos a disponibilidade de um leitor de código de barras ou mesmo não podemos utilizar, como mostrado no cenário. Neste artigo veremos apenas como fazer a leitura de um código de barras através de uma imagem que contenha tal código, mas vale ressaltar que o Zxing também realiza a criação de código de barras a partir do valor informado.

O primeiro passo para usar a API Zxing no nosso projeto é fazer a importação das bibliotecas necessárias (o link de download está no final do artigo). Coloque estas no classpath do seu projeto para que possa usar as classes que veremos mais a frente.

Vejamos agora como ficará a interface da nossa aplicação na Figura 2.

Figura 2. Interface da nossa aplicação

A nossa aplicação contém quatro componentes que serão de grande utilidade: o primeiro é o campo de texto onde será mostrado o caminho da imagem que selecionamos, o segundo é o resultado do valor extraído do código de barras, o terceiro é o botão 'Selecionar Imagem' que nos possibilita selecionar uma imagem do nosso computador e o quarto botão 'Processar' inicia o processamento do Zxing que fará a leitura do código de barras e extração dos valores necessários.

Agora vejamos como ficou nosso arquivo .form para quem desejar criar a interface apresentada na Figura 2, no Netbeans.

<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" type="org.netbeans.modules.form.forminfo .JFrameFormInfo"> <Properties> <Property name="defaultCloseOperation" type="int" value="3"/> </Properties> <SyntheticProperties> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> </SyntheticProperties> <AuxValues> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jTextFieldImagem" alignment="0" min="-2" pref="329" max="-2" attributes="0"/> <Component id="jTextFieldResultado" alignment="0" min="-2" pref="329" max="-2" attributes="0"/> <Group type="102" alignment="0" attributes="0"> <Component id="jButtonSelecionar" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jButtonProcessar" min="-2" max="-2" attributes="0"/> </Group> </Group> <EmptySpace pref="25" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Component id="jLabel1" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jTextFieldImagem" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="25" max="-2" attributes="0"/> <Component id="jLabel2" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jTextFieldResultado" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="20" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> <Component id="jButtonSelecionar" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jButtonProcessar" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="javax.swing.JLabel" name="jLabel1"> <Properties> <Property name="text" type="java.lang.String" value="Imagem"/> </Properties> </Component> <Component class="javax.swing.JLabel" name="jLabel2"> <Properties> <Property name="text" type="java.lang.String" value="Resultado"/> </Properties> </Component> <Component class="javax.swing.JTextField" name="jTextFieldImagem"> <Properties> <Property name="enabled" type="boolean" value="false"/> </Properties> </Component> <Component class="javax.swing.JTextField" name="jTextFieldResultado"> </Component> <Component class="javax.swing.JButton" name="jButtonSelecionar"> <Properties> <Property name="text" type="java.lang.String" value="Selecionar Imagem"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonSelecionarActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="jButtonProcessar"> <Properties> <Property name="text" type="java.lang.String" value="Processar"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonProcessarActionPerformed"/> </Events> </Component> </SubComponents> </Form>
Listagem 1. Fzxing.form

O código acima dispensa comentário, pois ele servirá apenas para que você possa reproduzir a mesma interface no seu ambiente de desenvolvimento, caso seja o Netbeans.

Enfim começaremos a construir os métodos de cada botão acima e depois mostraremos como ficou nossa classe Fzxing por completo.

private void jButtonSelecionarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSelecionarActionPerformed JFileChooser fChooser = new JFileChooser(); fChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); if (fChooser.showSaveDialog(this) != 1){ File fImage = fChooser.getSelectedFile(); try { buffImage = ImageIO.read(fImage); jTextFieldImagem.setText(fImage.getAbsolutePath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }//GEN-LAST:event_jButtonSelecionarActionPerformed
Listagem 2. Método do botão 'Selecionar Imagem'

O nosso botão jButtonSelecionar possui um listener que dispara o método jButtonSelecionarActionPerformed(), que é chamado toda vez que o usuário clicar no botão. Na Listagem 2 temos exatamente a demonstração do método que possibilita a seleção da imagem. Logo na primeira e segunda linhas temos a criação de um objeto JfileChooser e a configuração do modo de seleção para FILES_ONLY, assim, restringimos a escolha do usuário apenas para arquivos e não diretórios. Continuando a codificação, nós temos uma condição que abre um diálogo do tipo 'saveDialog' e, caso o usuário escolha um arquivo, em vez de clicar em cancelar, o resultado será diferente de 1 e entramos na condição proposta.

Dentro da condição proposta nós capturamos o arquivo escolhido através do fchooser.getSelectedFile() e logo depois transformamos o arquivo, que deve ser uma imagem, em um BufferedImage, não esquecendo de mostrar o caminho da imagem no campo jTextFieldImagem. Todo esse processo faz com que armazenemos a imagem selecionada em um objeto do tipo BufferedImage que está declarado no início da nossa classe Fzxing, ou seja, um atributo de instância.

private void jButtonProcessarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonProcessarActionPerformed try { if (buffImage == null){ JOptionPane.showMessageDialog(this, "Você deve escolher a imagem !!"); return; } //1 LuminanceSource source = new BufferedImageLuminanceSource(buffImage); //2 BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); //3 Reader reader = new MultiFormatReader(); //4 Result result = reader.decode(bitmap); jTextFieldResultado.setText(result.getText()); } catch (NotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ChecksumException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } }//GEN-LAST:event_jButtonProcessarActionPerformed
Listagem 3. Método do botão processar

Nós só podemos processar a imagem caso haja alguma, e sendo assim, a primeira checagem da Listagem 3é conferir se o objeto 'buffImage' não é nulo. As próximas linhas são dedicadas a funções exclusivas do Zxing que estão numeradas com linhas para facilitar a explicação:

Para finalizar o artigo, iremos mostrar na Listagem 4 o código completo da classe Fzxing com todos os detalhes e implementações explicados anteriormente, para que você leitor possa utilizá-la como meio de estudo.

package br.com.dev.gui; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import com.google.zxing.BinaryBitmap; import com.google.zxing.ChecksumException; import com.google.zxing.FormatException; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.NotFoundException; import com.google.zxing.Reader; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; public class FZxing extends javax.swing.JFrame { private BufferedImage buffImage; /** Creates new form FZxing */ public FZxing() { initComponents(); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc=" Código Gerado ">//GEN-BEGIN:initComponents private void initComponents() { jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jTextFieldImagem = new javax.swing.JTextField(); jTextFieldResultado = new javax.swing.JTextField(); jButtonSelecionar = new javax.swing.JButton(); jButtonProcessar = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); jLabel1.setText("Imagem"); jLabel2.setText("Resultado"); jTextFieldImagem.setEnabled(false); jButtonSelecionar.setText("Selecionar Imagem"); jButtonSelecionar.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonSelecionarActionPerformed(evt); } }); jButtonProcessar.setText("Processar"); jButtonProcessar.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonProcessarActionPerformed(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout .Alignment.LEADING) .addComponent(jLabel1) .addComponent(jLabel2) .addComponent(jTextFieldImagem, javax.swing.GroupLayout .PREFERRED_SIZE, 329, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jTextFieldResultado, javax.swing.GroupLayout .PREFERRED_SIZE, 329, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addComponent(jButtonSelecionar) .addPreferredGap(javax.swing.LayoutStyle .ComponentPlacement.RELATED) .addComponent(jButtonProcessar))) .addContainerGap(25, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jTextFieldImagem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(25, 25, 25) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jTextFieldResultado, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(20, 20, 20) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButtonSelecionar) .addComponent(jButtonProcessar)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pack(); }// </editor-fold>//GEN-END:initComponents private void jButtonProcessarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonProcessarActionPerformed try { if (buffImage == null){ JOptionPane.showMessageDialog(this, "Você deve escolher a imagem !!"); return; } LuminanceSource source = new BufferedImageLuminanceSource(buffImage); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Reader reader = new MultiFormatReader(); Result result = reader.decode(bitmap); jTextFieldResultado.setText(result.getText()); } catch (NotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ChecksumException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } }//GEN-LAST:event_jButtonProcessarActionPerformed private void jButtonSelecionarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSelecionarActionPerformed JFileChooser fChooser = new JFileChooser(); fChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); if (fChooser.showSaveDialog(this) != 1){ File fImage = fChooser.getSelectedFile(); try { buffImage = ImageIO.read(fImage); jTextFieldImagem.setText(fImage.getAbsolutePath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }//GEN-LAST:event_jButtonSelecionarActionPerformed /** * @param args the command line arguments */ public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new FZxing().setVisible(true); } }); } // Declaração de variáveis - não modifique//GEN-BEGIN:variables private javax.swing.JButton jButtonProcessar; private javax.swing.JButton jButtonSelecionar; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JTextField jTextFieldImagem; private javax.swing.JTextField jTextFieldResultado; // Fim da declaração de variáveis//GEN-END:variables }
Listagem 4. Fzxing.java completa

Este artigo teve como principal objetivo demonstrar o uso da API Zxing de forma prática, rápida e eficaz. Assim, você caro leitor, poderá usar tal biblioteca em seus projetos que precisam realizar leitura ou escrita de código de barras em Java.

Artigos relacionados