Imprimindo com PHP na impressora não fiscal Bematech MP-20 MI

Alguns meses atrás eu precisei implementar em um de meus sistemas PHP a impressão de O.S. de um cliente na impressora Bematech MP-20 MI.

Se você já precisou alguma fez implementar alguma coisa para esta impressora, sabe que ela tem um modo particular de trabalhar, que não basta simplesmente mandar imprimir a partir do browser e ser feliz. O grande problema para nós que trabalhamos a partir de um browser, é que nem todos os browsers conseguem se comunicar com esta impressora da forma que ela exige, para ser mais exato, a única forma que encontrei na internet de fazer esta impressora se comunicar com o browser foi instalar um DLL na máquina, criar uma chave de registro no Windows e chamar a impressão através do IE com javascript para conseguir utilizar a DLL e chave de registro necessária para comunicar com a impressora.

Como eu não uso Windows, uso Ubuntu Linux, nem queria ter o trabalho de fazer toda essa configuração em todas as máquinas do cliente, resolvi ir contra este único tutorial que encontrei na net (inclusive no suporte da Bematech) e procurar uma solução mais simples de implementar e que também funcione em máquina que não usa o Windows.

Esta impressora não consegue imprimir arquivos PDF, pois não é possível coloca-la em mode gráfico sem utilizar a DLL de comunicação com ela. Então criar um PDF com o tamanho exato da O.S. e mandar imprimir também não deu muito certo. Como a impressora por padrão vem configurada como modo texto, tentei imprimir direto do browser, fazendo uma página de texto puro com o conteúdo da O.S., mas também não deu muito certo, pois a impressora sempre achava que o conteúdo do texto a ser impresso tinha o tamanho de uma A4, ou seja, desperdiçava uma quantidade considerável de bulbina na margem inferior das O.S.

Foi aí que resolvi testar imprimir pelo bloco de notas para ver se por lá também ia pegar o papel como A4. Para minha surpresa, o bloco de notas tem uma opção em sua configuração de impressão em que você marca um checkbox que faz o corte automático do papel quando a última linha é impressa.

Resumindo a história, para resolver o problema bastou então eu gerar as O.S. em TXT, mandar o TXT para download e instruir o cliente a imprimir pelo TXT que foi baixado.

Segue abaixo um exemplo de como gerar o TXT e manda-lo para download, incluindo as especificações necessária para imprimir na bulbina, como quantidade de colunas por linha, funções para formatação de espaços e outras coisinha que desenvolvi e resolvi compartilhar com todos que precisem de uma solução parecida com esta.

<?php
        
    /*
	 * Gerar um arquivo .txt para imprimir na impressora Bematech MP-20 MI
	 */

        $n_colunas = 40; // 40 colunas por linha
        
        /**
         * Adiciona a quantidade necessaria de espaços no inicio 
         * da string informada para deixa-la centralizada na tela
         * 
         * @global int $n_colunas Numero maximo de caracteres aceitos
         * @param string $info String a ser centralizada
         * @return string
         */
        function centraliza($info)
        {
            global $n_colunas;
            
            $aux = strlen($info);
            
            if ($aux < $n_colunas) {
                // calcula quantos espaços devem ser adicionados
                // antes da string para deixa-la centralizada
                $espacos = floor(($n_colunas - $aux) / 2);
                
                $espaco = '';
                for ($i = 0; $i < $espacos; $i++){
                    $espaco .= ' ';
                }
                
                // retorna a string com os espaços necessários para centraliza-la
                return $espaco.$info;
                
            } else {
                // se for maior ou igual ao número de colunas
                // retorna a string cortada com o número máximo de colunas.
                return substr($info, 0, $n_colunas);
            }
            
        }
        
        /**
         * Adiciona a quantidade de espaços informados na String
         * passada na possição informada.
         * 
         * Se a string informada for maior que a quantidade de posições
         * informada, então corta a string para ela ter a quantidade
         * de caracteres exata das posições.
         * 
         * @param string $string String a ter os espaços adicionados.
         * @param int $posicoes Qtde de posições da coluna
         * @param string $onde Onde será adicionar os espaços. I (inicio) ou F (final).
         * @return string
         */
        function addEspacos($string, $posicoes, $onde)
        {
            
            $aux = strlen($string);
            
            if ($aux >= $posicoes)
                return substr ($string, 0, $posicoes);
            
            $dif = $posicoes - $aux;
            
            $espacos = '';
            
            for($i = 0; $i < $dif; $i++) {
                $espacos .= ' ';
            }
            
            if ($onde === 'I')
                return $espacos.$string;
            else
                return $string.$espacos;
            
        }
        
        $txt_cabecalho = array();
        $txt_itens = array();
        $txt_valor_total = '';
        $txt_rodape = array();
        
        $txt_cabecalho[] = 'Razão social da empresa aqui'; 
        
        $txt_cabecalho[] = 'CNPJ da empresa aqui';
        
        $txt_cabecalho[] = ' '; // força pular uma linha entre o cabeçalho e os itens
        
        $txt_itens[] = array('Cod.', 'Produto', 'Env.', 'Qtd', 'V. UN', 'Total');
        
	$tot_itens = 0;

    	$txt_itens[] = array(251, 'Prod. linha 1', '002', 3, '0.50', '1.50');
	$tot_itens += 1.50;

    	$txt_itens[] = array(278, 'Prod. linha 2', '005', 1, '0.75', '0.75');
	$tot_itens += 0.75;

    	$txt_itens[] = array(553, 'Prod. linha 3', '014', 10, '1.50', '15.00');
	$tot_itens += 15.00;
        
        $aux_valor_total = 'Sub-total: '.$tot_itens;
        
	// calcula o total de espaços que deve ser adicionado antes do "Sub-total" para alinhado a esquerda
        $total_espacos = $n_colunas - strlen($aux_valor_total);
        
        $espacos = '';
        
        for($i = 0; $i < $total_espacos; $i++){
            $espacos .= ' ';
        }
        
        $txt_valor_total = $espacos.$aux_valor_total;
        
        $txt_rodape[] = 'Cód. Cliente: 150'; 
        
        $txt_rodape[] = 'CPF/CNPJ: 999.999.999-99'; 
        
        $txt_rodape[] = 'Nome do cliente';
        
        $txt_rodape[] = ' '; // força pular uma linha
        
        $txt_rodape[] = '________________________________________';
        
        $txt_rodape[] = '         Assinatura do Cliente          ';
        
	// centraliza todas as posições do array $txt_cabecalho
        $cabecalho = array_map("centraliza", $txt_cabecalho);
        
	/* para cada linha de item (array) existente no array $txt_itens,
	 * adiciona cada posição da linha em um novo array $itens
	 * fazendo a formatação dos espaçamentos entre cada coluna
	 * da linha através da função "addEspacos"
	 */
        foreach ($txt_itens as $item) {
            
            /*
	     * Cod. => máximo de 5 colunas
	     * Produto => máximo de 11 colunas
	     * Env. => máximo de 6 colunas
	     * Qtd => máximo de 4 colunas
	     * V. UN => máximo de 7 colunas
	     * Total => máximo de 7 colunas
	     *
	     * $itens[] = 'Cod. Produto      Env. Qtd  V. UN  Total'
	     */
            
            $itens[] = addEspacos($item[0], 5, 'F')
                    . addEspacos($item[1], 11, 'F')
                    . addEspacos($item[2], 6, 'I')
                    . addEspacos($item[3], 4, 'I')
                    . addEspacos($item[4], 7, 'I')
                    . addEspacos($item[5], 7, 'I')
                ;
            
        }
        
	/* concatena o cabelhaço, os itens, o sub-total e rodapé
	 * adicionando uma quebra de linha "\r\n" ao final de cada
	 * item dos arrays $cabecalho, $itens, $txt_rodape
	 */
        $txt = implode("\r\n", $cabecalho)
            . "\r\n"
            . implode("\r\n", $itens)
            . "\r\n"
            . $txt_valor_total // Sub-total
            . "\r\n\r\n"
            . implode("\r\n", $txt_rodape);
        
        // caminho e nome onde o TXT será criado no servidor
        $file = 'uploads/nome_arquivo.txt';

        // cria o arquivo
        $_file  = fopen($file,"w");
        fwrite($_file,$txt);
        fclose($_file);

        header("Pragma: public"); 
        // Força o header para salvar o arquivo
        header("Content-type: application/save");
        header("X-Download-Options: noopen "); // For IE8
        header("X-Content-Type-Options: nosniff"); // For IE8
        // Pré define o nome do arquivo
        header("Content-Disposition: attachment; filename=imprimir.txt"); 
        header("Expires: 0"); 
        header("Pragma: no-cache");

        // Lê o arquivo para download
        readfile($file);
        
        exit;

O código é bem simples e ainda está comentado.

Espero ter ajudado.

Marcos de P. Muniz

Graduado em Sistemas de Informação pela UEMG, é Pós Graduado em Segurança da Informação pela Faculdade Pitágoras, é desenvolvedor PHP desde 2006, trabalha como Analista / Desenvolvedor em uma empresa que atua na área de ensino a distância, faz freelancer criando sites, sistemas e portais utilizando PHP, MySQL e jQuery.

Você pode gostar...

27 Resultados

  1. edsonvga disse:

    ola, indo na sua linha de raciocínio, será que não conseguiríamos trazer as informações do arquivo gerado para o browse e com javascript jogar direto na porta lpt1?

    • Olá, Obrigado pelo comentário.

      Bom, pelo que pesquisei a única forma que encontrei na internet de fazer esta impressora se comunicar com o browser foi instalar um DLL na máquina, criar uma chave de registro no Windows e chamar a impressão através do IE com javascript para conseguir utilizar a DLL e chave de registro necessária para comunicar com a impressora. Só que esse processo de instalação de DLL + chave de registro deve ser feito em todos os computadores que irão utilizar a impressora, do meu ponto de vista isso geraria um grande transtorno, já que sempre que precisasse formatar o computador seria necessário voltar na empresa para reconfigurar aquela máquina.

      Não sei se seria possível enviar as informações por javascript direto para a porta LPT1 sem utilizar uma DLL para fazer a comunicação, acredito que por medida de segurança esse tipo de acesso direto ao hardware não deva ser permitido, mas não tenho certeza.

      Se você conseguir encontrar uma forma mais simples para realizar as impressões comente ela aqui, terei o maior prazer em atualizar este Post e citar seu nome como co-autor.

  2. Carlos Henrique disse:

    Olá, gostei da sua solução e de ter compartilhado. Pensando no problema da impressão direta do browser será que não seria possível a criação de um script que estaria em loop lendo um arquivo arquivo TXT e enviando o conteúdo desse arquivo para impressora com o comando LPR???

    • Olá Carlos Henrique,

      Obrigado pelo comentário.

      Eu não conhecia o comando LPR, mas depois de seu comentário deu uma pesquisada rápida e vi que é sim possível enviar as informações diretamente para a impressora através do conjunto de funções Printer do PHP, segundo o próprio manual online do PHP, mas pelo teste rápido que fiz aqui, percebi que é necessário instalar ou ativar a extensão Printer e configura-la no php.ini para que ela funcione.

      Não foi possível fazer o teste com a impressora MP-20 MI da Bematech porque eu não tenho ela aqui, só no meu cliente. Seria interessante testar este recurso com a impressora. Se for realmente possível enviar impressão para ela é só adaptar o meu código para enviar as linhas direto para impressão ao invés de concatenar tudo para gerar o TXT.

      • Carlos Henrique disse:

        Obrigado pela resposta. Caso você consiga testar o comando na impressora depois conta como foi. Fiz a pergunta porque já pensava em utilizar esse comando mas não tenho como testar em lugar nenhum. Independente do resultado posta depois para ficarmos sabendo.
        Valeu

  3. eduardo disse:

    Olá, sou novo em PHP, li seu artigo mas não intendi muito como utilizá-lo, se puder me ajudar em implementar-lo ficarei muito grato, comprei uma MP20 só pra fazer testes e até hoje não consegui imprimir algo .

    • Olá Eduardo, obrigado pelo comentário.

      O código, como eu disse no post é muito simples, mas para quem já conhece PHP. No seu caso, como você é novo em PHP o mais certo seria você procurar alguma pessoa aí na sua cidade que saiba PHP para te ajudar a implementa-lo. Explicar a implementação dessa solução pela internet para um usuário iniciante é muito complexo.

  4. José disse:

    Legal a tua solução. Tu comenta que há uma opção no bloco de notas que faz o corte automático? Onde fica essa opção? O programa é o bloco de notas do windows mesmo?.

    • Olá José, obrigado pelo comentário.

      Verifique se só com os passos abaixo sua impressora vai parar de imprimir na hora certa, realizando assim o corte da impressão sem desperdiçar papel.

      1. Vá nas propriedades da impressora (aqui eu instalei com driver Generic / Text Only).
      2. Comandos da Impressora.
      3. Iniciar trabalho de impressão.
      4. Digite o seguinte: <1B>C<01>

      Obs.: A número 01 significa o número de linhas que a impressora irá avançar o papel.

      Posta aqui depois se essa solução deu certo para você.

      • José disse:

        Não cheguei a testar isso. Aqui a impressora já está cortando. Não estava cortando antes porque a impressão estava configurada para imprimir rodapé.

  5. Edson Rodrigues disse:

    Olá pessoal,

    Já testei a DLL MP2032 para Windows 7 e funciona o problema que tem que Utilizar o Ruindows.

    Estou a procura de uma solução que funciona no Linux Ubuntu.

    Hoje tenho a seguinte Estrutura;
    SO Ubuntu 12.04 32 Bits
    Impressora: MP-4200 TH
    Driver_CUPS_Linux_x86

    Isto funciona muito bem.
    Eu gero um Modal com os dados que quero imprimir e mando para impressora.

    Mas existe um problema, desta forma não tenho controle total da impressão. Exemplo, não consigo fazer a checagem da impressora antes de imprimir como: se tem papel antes de mandar imprimir, se a impressora esta ligar. Isto esta me trazendo grandes transtornos. Isto soh é possível quando utilizo a MP2032 mas como soh funciona em Windows.

  6. Fabio disse:

    Pessoal, alguém já enviou comandos via PHP com comandos em Hexa no padrão ECS/POS ?
    Já consegui imprimir via stream_socket_client (sem instalar a impressora no Ubuntu!) mas apenas texto.
    Quero enviar comandos para criar cód de barras e formatar texto.

    abs
    Fabio

  7. Rafael Pereira disse:

    Olá Marcos, pelo que eu entendi você escreve um arquivo texto puro direto na porta de impressão, correto?

    Eu estou desenvolvendo o Cupom Fiscal Eletrônico e o mesmo deve ser impresso em impressoras não fiscais, porém eu tenho um problema o no final do Cupom Fiscal Eletrônico tem que mostrar um QR Code que redireciona para a página do Sefaz, a principio eu tenho os QR Code de cada cupom armazenados em JPG, você sabe se é possível de alguma forma enviar uma imagem para um impressora não fiscal.

    Desde já agradeço.

    • Olá Rafael, obrigado pelo comentário.

      Respondendo suas dúvidas, primeiro eu não mando o arquivo direto para porta de impressão, eu crio um arquivo .txt e inicio o download desse arquivo criado. Depois de baixado o arquivo, eu mando imprimi-lo normalmente com Ctrl+P por exemplo.

      Sobre a segunda dúvida, essas impressoras trabalham também com um modo gráfico, possibilitando, por exemplo, imprimir um código de barras no cupom fiscal. Eu não sei se a definição da impressão do modo gráfico seria suficiente para que o QR Code ficasse legível pelos leitores de QR Code, mas o grande problema nem é esse, o grande problema é você colocar a sua impressora para imprimir no modo gráfico usando PHP, pois para fazer isso é necessário passar comandos de configuração diretamente para impressora no instante do envio da impressão. O problema que encontrei quando tentei fazer isso a algum tempo atrás foi que não consegui fazer o PHP se comunicar diretamente com a impressora de forma que possibilitasse o envio dos comandos de configuração.

      Espero ter ajudado.

  8. diego disse:

    Olá, eu gostaria de aumentar a fonte, como eu posso fazer isso?

  9. Moises Duarte disse:

    Na realidade, vocês ainda podem melhorar esse script
    escrevendo o arquivo txt direto na LPT1.

    Um modo simples de fazer isso se o servidor for Windows
    seria utilizar o comando exec(“print c:\wamp\www\seu_sistema\seu_arquivo.txt /c /d:lpt1”);

    Eu já fiz e deu certo.

  10. Oliveiros disse:

    Uso ubuntu 14
    Imprimo com php da seguinte forma:
    Gero um arquivo pdf e imprimo com o lpr, desta forma a impressão vai direto
    Como a impressora está perto de mim, vejo na informação do led se tem papel ou não

  11. Décio Rocha disse:

    Olá, Marcos!!!
    Tenho uma Mecaf não fiscal, com 40 colunas.
    Meu projeto prevê PDVs em 15 a 20 pontos diferentes, inclusive usando tablets.
    O servidor é um Linux Turnkey para Joomla 3.0.
    Acha que consigo fazer uma impressão direta para a impressora com este script que vc passou com um simples botão de envio???

    • Olá Décio,

      É possível sim, só que você terá que dar uma olhada na documentação do PHP para ver como enviar comandos diretamente para impressora usando comandos do sistema operacional, pois creio que diretamente do PHP para a impressora não seja possível.

  12. cleiton disse:

    Marcos excelente!
    Teria alguma forma de setar o arquivo gerado sem rodape e cabeçalho ?
    pq toda vez que vou imprimir sai la em baixo.. “pagina 1”

  13. Anderson disse:

    Como faço para ele ler as informações já imprimir direto?

    ATT

  14. renato disse:

    Olá Marcos parabens pela tutorial excelente, estou usando seu codigo porem como minha impressora é uma vox elgin termica não fiscal 80 colunas, esta imprimindo tudo muito espaçado, o resultado acaba saindo em 2 paginas, alterei para 80 o numero de colunas, mais sem sucesso, vc tem alguma dica??

Deixe uma resposta

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