Wednesday, November 7, 2012

Conectando sua aplicação Java a um Scanner


Scanners, câmeras digitais, e outros dispositivos de aquisição de imagens estão constantemente presentes no panorama da computação. Em que pese essa ubiquidade, Java ainda não possui uma especificação ou uma API padrão para interagir com estes componentes.

Quando se faz necessário integrar uma aplicação Java a tais dispositivos, o programador deve recorrer a bindings, que usam métodos nativos fora da JVM.

Atualmente existem dois protocolos, conhecidos como TWAIN e SANE, que especificam a comunicação entre um software e um dispositivo de imagens.

TWAIN, além de um protocolo, também representa uma organização formada pelas principais marcas da indústria da imagem. SANE (Scanner Access Now Easy), comumente usade em Linux, é uma API que padroniza o acesso a dispositivos de imagens, e difere de TWAIN a medida em que separa claramente o frontend (programa do usuário) e o backend (driver), o que torna mais fácil e transparente o acesso aos dispositivos dentro de uma rede (como uma LAN cheia de máquinas, mas com scanners conectados a apenas um ou duas máquinas, por exemplo).

Segue uma implementação simples que exibe uma janela e um botão que ativa o scanner local para digitalizar o documento contido bandeja. E necessário configurar seu classpath com as bibliotecas do projeto mm's computing, Scan_.jar e uk.co.mmscomputing.device.twain.jar, as quais podem ser baixadas aqui. Use JDK 7 32bit.

public class ScannerImageTest extends JFrame implements ScannerListener{

    Scanner scanner;
    ImagePanel imagePanel;
    JButton capturar;
    
    public ScannerImageTest() {                        
        setUpJFrame();
        //busca um scanner conectado à máquina local
        scanner = Scanner.getDevice();
        //registra um objeto ScannerListener para capturar os eventos
        scanner.addListener(this);
        setVisible(true);        
    }        

    private void setUpJFrame() {
        imagePanel = new ImagePanel();        
        capturar = new JButton("Capturar Imagem");
        capturar.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    //inicia a digitalização da imagem
                    scanner.acquire();
                } catch (ScannerIOException ex) {
                    ex.printStackTrace();
                }
            }
        });                    
        
        setSize(410, 510);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setLayout(new BorderLayout());                
        getContentPane().add(capturar, BorderLayout.NORTH);
        getContentPane().add(imagePanel, BorderLayout.CENTER);
        setTitle("Hello Scanner!");
}

    //chamado automaticamente pelo listener do scanner
    @Override
    public void update(ScannerIOMetadata.Type type, ScannerIOMetadata siom) {
        if(type.equals(ScannerIOMetadata.ACQUIRED)){            
            //neste ponto o documento foi totalmente digitalizado
            BufferedImage bufferedImage = siom.getImage();
            imagePanel.setScannedImage(bufferedImage);
        }
    }
    
    public static void main(String[] args) {
        //inicia a aplicação
        new ScannerImageTest();
    }
}

class ImagePanel extends JPanel{
    
    Image scannedImage;

    public void setScannedImage(Image scannedImage) {
        this.scannedImage = scannedImage;
        if(scannedImage != null){
            this.scannedImage = scannedImage.getScaledInstance(400, 500, 0);
        }
        super.repaint();
    }
    
    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);  
      if(scannedImage != null){        
        Graphics2D g2d = (Graphics2D) g.create();        
        g2d.drawImage(scannedImage, 0, 0, null);
        g2d.dispose();
      }
    }        
}

A aplicação captura a imagem e a exibe em um painel:




A biblioteca do projeto mm's computing permite que o programador utilize uma interface gráfica padrão, com mais recursos de digitalização, neste caso uma nova janela é exibida durante o processo; ou é possível acoplar uma interface mais simples no frame, chamando o método:

Scanner.getDevice().getScanGUI()