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.

Ilustração de um Código de Barras
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.

Interface da nossa aplicação
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:

  • As linhas 1 e 2 fazem o tratamento necessário na imagem para só então processá-la em um 'Reader' específico do Zxing. No nosso caso, na linha 3 nós criamos um MultiFormatReader que possibilita a leitura de código de barras em diversos formatos (Code 39, EAN-13 …), mas você pode criar um tipo específico de Reader caso ache necessário.
  • Na linha 4 nós usamos o Reader criado na linha 3 para chamar o método 'decode()'que retorna os dados encontrados no código de barras, O retorno deste método é um Result que contém diversos método como o getText(), que retorna o dado em forma de string, e o getBarcodeFormat() que retorna o formato do código de barras e etc.

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.