Do site para a
impressora, sem diálogo.
O EasyPrint é um programa leve que roda no computador da loja e deixa sites, ERPs e lojas virtuais imprimirem cupons, etiquetas, PDFs e HTML direto na impressora, com acentos certos e em português. Funciona no Windows e no Linux, e é open source.
Instalar e configurar
Você não precisa entender de tecnologia. O assistente faz as perguntas certas e testa a impressão com você.
- Baixe e abra o
EasyPrint.exeEle se instala sozinho na sua pasta de usuário. Não pede senha de administrador e não mexe no firewall. - Escolha a impressoraO EasyPrint lista as impressoras do computador e reconhece as térmicas mais comuns (Epson TM, Elgin i9, Bematech, Daruma, Zebra…). Ainda sem impressora? Use o Simulador, que mostra o cupom na tela.
- Confirme o tipo e o papelCupom (58 ou 80 mm), etiqueta ou folha A4. A opção detectada já vem marcada.
- Imprima o testeOs acentos saíram estranhos? Clique em "Os acentos saíram estranhos" e o EasyPrint testa outra tabela de caracteres. O texto saiu cortado? Ele troca a largura do papel.
No Linux
Baixe o easyprint-linux-x64 e rode. Ele se instala em ~/.local/bin e cria o atalho no menu de aplicativos:
curl -fsSL https://github.com/GuiVicS/easyprint/raw/main/install.sh | sh
Impressoras do CUPS aparecem automaticamente. Térmicas USB sem fila no CUPS aparecem como "USB direto (lp0)". Para PDF em térmica ou etiqueta, instale o poppler-utils. Para HTML, o Chrome ou o Chromium.
As duas perguntas que vão aparecer
Na primeira vez que o sistema da loja tentar imprimir, duas janelas pedem sua confirmação. Responda Permitir nas duas.
| Quem pergunta | O que aparece | Por quê |
|---|---|---|
| EasyPrint | "Um site quer usar suas impressoras" | Você decide quais sites podem imprimir. Só acontece uma vez por site. |
| Chrome / Edge | "Acessar outros apps e serviços neste dispositivo" | Proteção do navegador para sites que conversam com programas do computador (Local Network Access, 2026). |
Primeira impressão em 5 minutos
- Rode o EasyPrint e complete o assistenteEscolha o Simulador se não tiver impressora. O resultado aparece em Painel → Histórico.
- Adicione o SDKUm arquivo, sem dependências:
easyprint.js(comeasyprint.d.tspara TypeScript). Copie para o projeto. - Conecte uma vez e imprimaMonte o cupom com blocos. O EasyPrint converte para a linguagem da impressora.
<!-- 1. carregue o SDK (copie o arquivo para o seu projeto) --> <script src="/vendor/easyprint.js"></script> <span id="status"></span> <button onclick="imprimir()">Imprimir pedido</button> <script> // 2. conecte UMA vez const printer = EasyPrint.connect({ appName: 'Minha Loja' }); EasyPrint.widget(printer, '#status'); // selo "Impressora pronta" + ajuda // 3. imprima async function imprimir() { try { await printer.print( EasyPrint.receipt() .title('PEDIDO #123') .line() .item(2, 'X-Burguer', 'R$ 59,80', 'sem cebola') .total('TOTAL', 'R$ 59,80') .qr('https://minhaloja.com/p/123') .cut() ); } catch (err) { alert(err.message + '\n' + (err.hint || '')); // toda falha explica como resolver } } </script>
Você não informa a impressora, o papel nem a codificação. Isso foi decidido pelo lojista no assistente. Seu código fica igual para uma térmica de 58 mm, uma de 80 mm ou uma impressora A4.
Playground do documento
Edite o JSON e veja o cupom se ajustar à largura do papel. A quebra de linha e as colunas usam o mesmo algoritmo do agente.
Documento universal
Um documento é uma lista de blocos. Cada bloco descreve o que imprimir e o EasyPrint decide como: ESC/POS na térmica, ZPL na etiqueta ou desenho na impressora comum.
| Bloco | Builder | Para que serve |
|---|---|---|
text | .text(v, {align, bold, size}) | Texto comum. size de 1 a 3, align left/center/right. Também .center() e .bold(). |
title | .title('PEDIDO #1') | Texto grande, centralizado e em negrito. |
line | .line('=') | Separador na largura exata do papel. |
row | .row('Entrega', 'R$ 5,00') | Rótulo à esquerda e valor à direita. |
item | .item(2, 'Pizza', 'R$ 90,00', 'borda recheada') | Linha de pedido com a observação recuada. |
total | .total('TOTAL', 'R$ 95,00') | Total em fonte dupla. |
qr | .qr(url, 6) | QR Code nativo da impressora. |
barcode | .barcode('7891234567895', 'EAN13') | Código de barras CODE128 ou EAN13. |
image | .image(await EasyPrint.image('/logo.png')) | Logo ou imagem, convertida para preto e branco com pontilhado. |
feed · cut · drawer | .feed(3).cut().drawer() | Avançar papel, cortar e abrir a gaveta de dinheiro. |
Precisa de controle total?
Use printer.printRaw(bytes) para mandar comandos ESC/POS, ZPL ou EPL exatamente como estão. Aceita Uint8Array, string binária, { hex } ou um array misto.
PDF, HTML e ZPL prontos
Seu sistema já gera a DANFE em PDF, a comanda em HTML ou recebe a etiqueta do marketplace em ZPL? Mande do jeito que está. O EasyPrint adapta para cada impressora.
| Você tem | Chamada | O que acontece |
|---|---|---|
| PDF (DANFE, boleto, etiqueta Correios/Shopee) | printer.printPdf(url, { route: 'danfe' }) | Renderizado pelo próprio Windows (ou pdftoppm no Linux). Na Zebra, cada página vira uma etiqueta. Na térmica, um cupom. |
| HTML (comanda, layout pronto) | printer.printHtml(elemento) | Renderizado num navegador isolado, com JavaScript desligado. Na térmica, ocupa a largura do papel e o espaço em branco do fim é cortado. |
| ZPL do Mercado Livre | printer.printRaw(zpl, { route: 'etiqueta' }) | Vai direto para a impressora, sem conversão. |
// a URL é baixada pelo navegador com os cookies do seu site (endpoints autenticados funcionam) await printer.printPdf('/api/notas/4821/danfe.pdf', { route: 'danfe', pages: '1' }); // comanda montada na própria tela, com os estilos da página await printer.printHtml(document.querySelector('#comanda'), { route: 'pedido' });
Códigos de barras e QR são convertidos sem pontilhado, para continuarem legíveis no leitor. Os limites são 25 MB de PDF e 5 MB de HTML.
Finalidades: cada coisa na sua impressora
Seu sistema diz o que quer imprimir, e o lojista escolhe onde. Etiqueta na Zebra, DANFE na laser, pedido na térmica da cozinha. O código não precisa saber o nome de nenhuma impressora.
const printer = EasyPrint.connect({
appName: 'Meu ERP',
routes: { pedido: 'Pedidos (cozinha)', etiqueta: 'Etiquetas de envio', danfe: 'Notas fiscais' },
});
await printer.printRaw(zpl, { route: 'etiqueta' }); // sai na impressora escolhida para "etiqueta"As finalidades aparecem sozinhas no painel do EasyPrint (Impressoras → Finalidades), com um seletor de impressora para cada uma. Enquanto o lojista não escolhe, a impressão sai na padrão e o resultado avisa com routedBy: 'default-fallback'.
Impressão pela nuvem
Para imprimir sem nenhuma tela aberta. Por exemplo: chegou um pedido às 3h e a etiqueta já sai na expedição. O seu back-end assina o trabalho, o relay guarda na fila e o EasyPrint da loja busca e imprime.
const { EasyPrintCloud } = require('./easyprint-cloud'); // SDK de servidor, sem dependências
const cloud = new EasyPrintCloud({ relayUrl, apiKey, privateKey, publicKey });
// 1x por loja: guarde deviceId + deviceSecret (segredo); o lojista cola o código (uso único)
const { deviceId, deviceSecret, connectionCode } = await cloud.createDevice('Loja Centro');
const device = { deviceId, deviceSecret };
// a qualquer momento, do servidor
await cloud.print(device, { route: 'etiqueta', raw: { text: zpl } }); // cifrado + assinado
await cloud.print(device, { route: 'danfe', pdf: pdfBuffer });Por que é seguro
- Assinatura Ed25519 em cada trabalho. A loja só aceita trabalhos assinados pela chave do seu sistema. Nem quem controla o relay consegue forjar uma impressão.
- Criptografia de ponta a ponta. O pedido é cifrado (AES-256-GCM) para uma chave gerada no computador da loja. O relay só vê bytes cifrados, sem nomes nem endereços.
- Código de uso único. Depois de conectar, o código não serve mais. E se o relay tentar trocar a chave da loja, o seu servidor percebe e não envia.
- Amarrado ao computador. Um trabalho emitido para a Loja A é recusado na Loja B.
- Validade e anti-replay. Trabalhos vencidos são recusados, e cada trabalho imprime uma única vez, mesmo após reiniciar.
- Gaveta bloqueada por padrão. O lojista libera "Abrir gaveta", "PDF/HTML" e "RAW" por conexão.
- Sem portas abertas. Só conexões de saída por HTTPS. O token fica protegido com DPAPI no Windows e em arquivo 0600 no Linux.
Use o relay de referência (cloud/relay-server.js) ou implemente o protocolo no seu back-end. A especificação está em reference/erp.md.
API do SDK
| Chamada | O que faz |
|---|---|
EasyPrint.connect(opts) | Conecta e devolve o cliente. Chamar de novo devolve o mesmo cliente. Opções: appName, routes, downloadUrl, token, ports, autoReconnect, debug. |
printer.print(doc, opts) | Imprime um builder, {blocks}, um array ou uma string. opts: route, printer, copies, jobName. |
printer.printPdf(src, opts) | PDF por URL, Blob ou bytes. opts: pages, fit, paperSize, route. |
printer.printHtml(html, opts) | HTML (string ou elemento da página). JavaScript desligado por padrão. |
printer.printText · printImage · printRaw | Atalhos para texto, imagem e bytes crus. |
printer.routes() | Finalidades e as impressoras escolhidas pelo lojista. |
printer.printers() | Lista de impressoras com tipo, papel e colunas. |
printer.on('status', fn) | Avisa quando o status muda. Devolve a função para cancelar. |
printer.ready() | Promise que resolve quando dá para imprimir. |
printer.diagnose() | { ok, status, title, message, steps[] } em pt-BR, pronto para exibir. |
EasyPrint.widget(printer, el) | Selo de status com instruções de correção. Isolado em Shadow DOM. |
EasyPrint.image(src) | Converte uma URL, <img> ou <canvas> em PNG para impressão. |
Os tipos completos estão em easyprint.d.ts. Funciona com <script>, import (Vite, Webpack, Next) e require (Node 22+).
Status e erros
Toda falha vem com code (para o seu código decidir), message (o que houve) e hint (como resolver, escrito para o lojista).
| Status | Significa | Mostre |
|---|---|---|
ready | Tudo certo. | Indicador verde. |
not_running | EasyPrint fechado ou não instalado. | Passos e link de download. O SDK tenta reconectar sozinho. |
awaiting_approval | Esperando o lojista clicar em Permitir. | "Confirme no EasyPrint". |
browser_blocked | O Chrome/Edge bloqueou o acesso local. | Cadeado → Acesso à rede local → Permitir. |
denied | O site foi bloqueado no EasyPrint. | Como desbloquear em "Sites e apps". |
unsupported_browser | Safari com HTTPS. | Sugerir Chrome, Edge ou Firefox. |
| Erro | Quando |
|---|---|
PRINTER_NOT_FOUND | A impressora pedida não existe. A hint lista as disponíveis. |
NO_DEFAULT_PRINTER | O assistente ainda não foi concluído. |
PRINT_FAILED · TIMEOUT | A impressora recusou ou não respondeu (papel, tampa, cabo, fila do Windows). |
INVALID_DOCUMENT | Bloco com tipo ou campo errado. É bug no código, corrija o documento. |
NOT_RUNNING · NOT_AUTHORIZED · DENIED | Equivalem aos status acima. |
Frameworks
O padrão é sempre o mesmo: um módulo que conecta uma vez, um componente que mostra o status e um try/catch que exibe a dica.
Deixe a IA integrar por você
Preparamos as instruções no formato que as IAs de programação entendem melhor: o que fazer, em que ordem, os modelos de código por framework e o que não fazer. A IA detecta o seu framework, adiciona o SDK, cria o serviço de impressão, o indicador de status e o tratamento de erros.
SKILL.md, as referências e o SDK.
Como usar em cada ferramenta
| Ferramenta | Onde colocar |
|---|---|
| Claude Code | Extraia o .zip em ~/.claude/skills/ (vale para todos os projetos) ou em .claude/skills/ do projeto. Depois peça: "integre impressão com EasyPrint". |
| Cursor / Windsurf | Coloque EASYPRINT_AI.md na raiz e cite @EASYPRINT_AI.md no chat. |
| GitHub Copilot | Salve como .github/instructions/easyprint.instructions.md ou anexe com #file. |
| ChatGPT · Gemini · Claude.ai | Anexe EASYPRINT_AI.md e cole o pedido abaixo. |
| Lovable · Bolt · v0 | Adicione o arquivo ao projeto (ou cole o conteúdo) e envie o pedido. |
Pedido para colar
Integre impressão direta com o EasyPrint neste projeto seguindo o EASYPRINT_AI.md. Imprima o pedido/recibo (número, itens com observação, totais, cliente, endereço e pagamento), mostre o status da impressora na barra superior e exiba a dica de correção quando a impressão falhar. Use o nome "NOME DO SISTEMA".
Segurança
O EasyPrint troca a cadeia de certificados de soluções antigas por regras simples de entender.
- Gaveta bloqueada e limite de impressões. Nenhum site abre a gaveta sem o lojista liberar. Cada origem tem limite de 120 impressões por minuto, o que segura um site invadido.
- Só local. Escuta apenas em
127.0.0.1. Nenhuma porta fica aberta para a rede. - Permissão por site. O navegador informa o endereço do site (
Origin) e a página não consegue falsificá-lo. Cada site novo precisa da aprovação do lojista. - Sem certificado raiz. Navegadores já tratam
ws://127.0.0.1como seguro, então não instalamos uma autoridade certificadora no Windows. - Painel protegido. Token secreto a cada execução, checagem de
Hostcontra DNS rebinding e deOrigincontra CSRF. - Apps sem navegador usam tokens revogáveis, guardados apenas como hash SHA-256.
- Arquivos maliciosos. HTML é renderizado sem scripts, sem rede local e sem acesso a arquivos do computador. Imagens têm limite de tamanho e proteção contra bomba de descompressão.
- Downloads verificáveis. A release publica o
SHA256SUMS, e o instalador do Linux confere o hash antes de instalar.
EasyPrint × QZ Tray
O QZ Tray é a referência do mercado e foi estudado para desenhar o EasyPrint. O QZ ainda tem acesso a serial, USB e HID para balanças e leitores. O EasyPrint foca em impressão simples, segura e pronta para ERP.
| QZ Tray 2.3 | EasyPrint 1.0 | |
|---|---|---|
| Instalação | Administrador, CA raiz no Windows, regra de firewall | Pasta do usuário, sem admin, sem CA |
| Tamanho | 140 MB (Java + JavaFX) | ~90 MB, um único executável |
| Remover o aviso de site não confiável | Certificado pago (a partir de US$ 449/ano) | Um clique em Permitir, grátis |
| Acentos em térmicas | Regressão na 2.3.0 (issue #1519) | CP860/CP850 explícitos + teste guiado |
| Idioma | Inglês | Português |
| Configuração | Pelo código do site | Assistente para o lojista |
| PDF e HTML | Sim | Sim, inclusive em térmica e etiqueta |
| Finalidades (impressora por tipo de documento) | Não | Sim, escolhidas pelo lojista |
| Impressão pelo servidor | Não | Sim, assinada e cifrada de ponta a ponta |
| Serial, USB e HID | Sim | Ainda não |
| Linux | Sim | Sim (CUPS, USB direto, PDF, HTML) |
| macOS | Maduro | Básico (via CUPS, sem binário pronto) |
| Licença | LGPL 2.1 | MIT |
Perguntas frequentes
Funciona com qual impressora?
Qualquer impressora instalada no Windows. Térmicas ESC/POS (Epson, Elgin, Bematech, Daruma, Tanca, Knup, genéricas POS-58/80), etiquetas ZPL (Zebra, Elgin L42) e impressoras comuns jato de tinta ou laser.
Os acentos saem errados. E agora?
No painel: Impressoras → Ajustar → Acentos. Teste CP860 e depois CP850. O passo de teste do assistente faz isso automaticamente.
Posso imprimir em duas impressoras (cozinha e caixa)?
Sim. Use printer.printers() para listar as impressoras, guarde a escolha nas configurações do seu sistema e passe { printer: 'Cozinha' } no print. Basta parte do nome.
E se o EasyPrint estiver fechado?
O SDK detecta, muda o status para not_running e reconecta sozinho quando o EasyPrint abre. Ative "Abrir quando o Windows iniciar" no painel para isso não acontecer.
Funciona no celular ou tablet?
Não. A impressão acontece no computador onde o EasyPrint está instalado. Para imprimir a partir do celular, envie o pedido para um computador da loja que tenha o EasyPrint aberto.
Funciona no Linux?
Sim. Use o binário easyprint-linux-x64 (ou o script de instalação). Impressoras do CUPS e térmicas USB (/dev/usb/lp*) são detectadas. Se aparecer erro de permissão no USB, rode sudo usermod -aG lp $USER e entre de novo na sessão.
Onde ficam as configurações?
No Windows, em %APPDATA%\EasyPrint\config.json. No Linux, em ~/.config/easyprint/config.json. O log easyprint.log fica na mesma pasta.