Série PDP: Pesadelos de Programador – Impressão Matricial

Daee pessoal, vou começar a postar a resolução de alguns problemas punks que encontrei durante minha vida de programador ^^. Começando pelo duro trabalho de fazer o Java imprimir dados em impressoras matriciais, no meu caso uma Okitada Microline 320. Para começar fui logo achando que o trabalho seria fácil, a meu ver era só montar um layout no iReports, gerar o .jasper, jogar no servidor acessar o pdf e mandar imprimir; quem me dera fosse tão fácil assim.

Os problemas começaram já na configuração da impressora, existe uma infinidade de drivers para impressora em questão, e conseguir configurar um que deixe a impressão aceitável vai consumir muito tempo e trabalho, além é claro de várias xícaras de café, cabelos brancos, palavrões ditos etc. etc..

A seguir descobri outro problema, mesmo como o melhor driver configurado a impressora não se comporta bem quando é necessário imprimir dados a partir de um pdf. A Okidata interpreta o pdf como se fosse uma imagem, e tenta converter essa imagem na impressão propriamente dita, mas ela não é muito feliz nessa tarefa, a impressão de números e letras fica bem estranha, e em alguns casos até ilegível.

Após mais algumas horas(várias), percebi que não teria alternativa, a solução para uma impressão de qualidade seria: Inserir comandos hexadecimais de impressão direto na porta(LPT) da impressora, então vamos ao trabalho, começando pelo material de apoio básico:

  1. Entender a API de impressão do Java.
  2. Conhecer os comandos básicos de impressão Hexadecimal, tambem conhecida como “printer control language”. A linguagem que utilizei na Okidata foi a Manual ESC/P2, a impressora possui esta forma de emulação.

Após estudar um pouco o material acima segui os seguintes passos:

  • Configurei a impressora no computador/servidor onde a aplicação Java vai rodar, como faremos a impressão direta o driver é irrelevante, então podemos adicionar um driver genérico desta impressora.
  • Configurar a impressora para utilizar o modo de emulação ESCP2. Verifique se a impressora esta com papel, pressione SHIFT + SEL. A impressora vai entrar em modo de seleção, pressione LF, a impressora vai imprimir o modo de emulação atual, vá apertando TEAR até o modo de emulação Epson® FX (ESC/P), pressione SHIFT + SEL para salvar e sair.
  • Criar uma classe Java para realizar a impressão.

A Classe Java.

A minha classe comentada é basicamente o seguinte:

[sourcecode language='java']public void imprimir() throws URISyntaxException, FileNotFoundException,
            PrintException, IOException
    {
        //VARIAVEL PARA ARMAZENAR AS INSTRUÇÕES DE IMPRESSÃO
        StringBuffer conteudo = "";

        //CRIA A STREAM A PARTIR DA STRING
        InputStream ps = null;

        ps = new ByteArrayInputStream(conteudo.getBytes());

        //CRIA O DOCUMENTO DE IMPRESSAO
        DocFlavor flavor = DocFlavor.INPUT_STREAM.POSTSCRIPT;

        //LOCALIZA AS IMPRESSORAS DISPONIVEIS NO SERVIDOR/PC
        PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);

        //CRIA UM SERVIÇO DE IMPRESSAO, NESTE PONTO A IMPRESSORA AINDA NAO ESTA DEFINIDA
        services = PrintServiceLookup.lookupPrintServices(null, null);

        //CRIA UM TRABALHO DE IMPRESSAO
        DocPrintJob job = null;

        //NESTE FOR EU FAÇO A SELEÇÃO DA IMPRESSORA, EU BUSCO A IMPRESSORA PELO NOME CONFIGURADO,
        // DESTA FORMA SE ALTERAR O IP OU SERVIDOR NAO É NECESSARIO MECHER NO CODIGO
        for (int i = 0; i < services.length; i++) 
        {

            PrintService pserv = services[i];

            if (pserv.getName().equalsIgnoreCase("Okidata"))
            {

                //INSTANCIA O TRABALHO DE IMPRESSAO
                job = services[i].createPrintJob();

                //INSTANCIA O DOCUMENTO
                Doc doc = new SimpleDoc(ps, flavor, null);

                //VERIFICA QUANDO O TRABALHO ESTA COMPLETO
                PrintJobWatcher pjDone = new PrintJobWatcher(job);

                //IMPRIME
                job.print(doc, null);

                //AGUARDA A CONCLUSAO DO TRABALHO
                pjDone.waitForDone();

                ps.close();
            }
        }
    }

    /**
    
     * Classe para controle de impressões em impressoras matriciais.
    
     */
    class PrintJobWatcher 
    {

        // true iff it is safe to close the print job's input stream
        boolean done = false;

        /**
         * @author Jean C Becker
         * @version 1.0
         * Método para verificar o trabalho de impressão em impressoras matriciais.
         *
         * @param DocPrintJog   Objeto com o trabalho de impressão.
         * @return          Não se aplica.
        
         */
        PrintJobWatcher(DocPrintJob job)
        {

            // Add a listener to the print job

            job.addPrintJobListener(new PrintJobAdapter()
            {

                public void printJobCanceled(PrintJobEvent pje)
                {
                    allDone();
                }

                public void printJobCompleted(PrintJobEvent pje)
                {
                    allDone();
                }

                public void printJobFailed(PrintJobEvent pje)
                {
                    allDone();
                }

                public void printJobNoMoreEvents(PrintJobEvent pje)
                {
                    allDone();
                }

                void allDone() {
                    synchronized (PrintJobWatcher.this)
                    {
                        done = true;
                        PrintJobWatcher.this.notify();
                    }
                }
            });
        }

        /**
         * @author Jean C Becker
         * @version 1.0
         * Método para aguardar a finalização do trabalho de impressao.
         * @return          Não se aplica.
        
         */
        public synchronized void waitForDone()
        {
            try {
                while (!done)
                {
                    wait();
                }
            } catch (InterruptedException e)
            {
            }
        }
    }[/sourcecode]

A parte mais trabalhosa é alimentar a String que é enviada para impressora, nesta String vão estar contidos todos os comandos e dados a serem impressos, basicamente você terá que desenvolver todo o layout - quebra de linha, ajustes horizontais, negrito, sublinhado, itálico etc.  - a partir de comandos Hexadecimais. A lógica é parecida com tags HTML, seguem abaixo algumas que considero importante, já no padrão Java:

"\u001b\u0040" - Inicio do conteúdo de impressão, define as configurações padrões de impressão.

"\u001B\u004A" + (char) valor - Deslocamento vertical do carro de impressão, valor é um conteúdo numérico que indica o tamanho do deslocamento em polegadas.

"\n\r" - Quebra linha.

Um pequeno exemplo:

String conteudo = "\u001b\u0040" + "TITULO PARA IMPRESSAO" + "\n\r";

conteudo += "IMPRIMINDO CONTEUDO EM IMPRESSORA MATRICIAL \n\r";

//AQUI A QUEBRA DE LINHA É MAIOR QUE A PADRÃO

conteudo += "IMPRIMINDO CONTEUDO EM IMPRESSORA MATRICIAL"+"\u001B\u004A" + (char) 100;

Bom, espero que esse post ajude quem estiver com os mesmo problemas que eu já tive, e no mais... Boa sorte =D

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.