Um app que imprime etiqueta de verdade — e a história de como ele chegou aqui
Isso aqui não é release note. É a história do freeLabel contada do começo: o problema, as tentativas que falharam, o bug que parecia impossível, as medições que ninguém faz, e o que já dá para fazer hoje com ele.
a tela de trabalho hoje — essa é a versão 1.52.0
Role para baixo. Cada capítulo tem print de verdade — nada de mockup.
Uma etiqueta, uma impressora emprestada e nenhuma senha de administrador
Tudo começou com uma situação bem comum em campo: preciso identificar equipamento — bomba, compressor, quadro elétrico, válvula — com etiqueta que aguente o ambiente e que dê para ler daqui a dois anos, com graxa em cima. A solução óbvia é uma impressora térmica de etiquetas. A que apareceu foi uma Seiko SLP650.
O problema não é a impressora. É o caminho até ela:
- O software oficial quer instalar driver e mexer no sistema — coisa que exige senha de admin, e o computador em uso não é meu.
- Sem o driver, o macOS “vê” a impressora, mas imprime lixo: manda texto, sai página em branco; manda PNG, sai uma tarja preta. Cada impressora térmica tem uma linguagem própria.
- O app oficial da SII é pesado, preso à versão do sistema, e não faz o que eu queria: lote a partir de planilha, modelo salvo em arquivo, layout em milímetros.
O filtro oficial da SII + o driver que o macOS já tem
Depois de muita tentativa (e bastante papel jogado fora), apareceu o caminho: a própria SII distribui um
filtro CUPS — um programinha que converte imagem em comandos da impressora. Ele se chama
rastertosiislp e vem dentro do pacote oficial, que é só um monte de arquivos.
A ideia então foi simples e teimosa: se o problema é “instalar”, então não instale nada. Copiar o filtro oficial para uma pasta comum, usar o backend USB que já existe no macOS, e chamar a impressão por script — tudo dentro da pasta do usuário, sem sudo, sem driver assinado, sem tocar no sistema.
O que passou a funcionar
- Enviar PNG em 203 dpi direto para a SLP650
- Escolher o tamanho do rolo (
SRLDPI) pelo próprio comando - Rodar de pendrive, de pasta solta, sem instalar
- Ver o log de cada impressão e saber por que falhou
O que o macOS ainda atrapalha
As pastas Downloads, Desktop e Documents são protegidas (TCC):
um app de fora não pode executar arquivos de lá. O app detecta, avisa e manda mover a pasta para
~/freeLabel — foi a primeira de uma longa série de brigas com o sistema.
Nasceram aí os primeiros comandos numerados: 0-liberar (tira a quarentena),
1-diagnostico, 2-imprimir, 3-teste-minimo — que imprime um SLP de teste para separar
“problema de transporte” de “problema de conversão”. Até hoje eles estão no pacote, porque diagnóstico é metade do trabalho.
Se já sei mandar pixels, então dá para desenhar a etiqueta
Com o caminho de impressão resolvido, o próximo problema era óbvio: eu estava desenhando etiqueta em editor de imagem, medindo pixel na régua, torcendo para o texto caber. Isso não escala.
A escolha foi radical de propósito: um único arquivo HTML, aberto numa janela própria, sem servidor de internet, sem build complexo. Todas as bibliotecas (código de barras, QR, leitor de planilha) ficam dentro do arquivo. Consequência: funciona sem internet, funciona em Mac antigo, funciona se você copiar a pasta para um pendrive.
Por que milímetros importam tanto
A etiqueta é 54 × 101 mm (rolo SLP-SRL). A térmica imprime a 203 dpi, o que dá exatamente 8 pontos por milímetro. Se o layout fosse em pixel de tela, cada zoom bagunçaria a medida. Em milímetros, o app sabe que 1 mm = 8 pontos e consegue gerar o PNG já no tamanho final de impressão — sem “ajustar à página” e sem sair deslocado.
Detalhe técnico: a área imprimível não é a etiqueta inteira
x 2,66 → 51,34 mm e y 2,70 → 98,30 mm. O app guarda essa tabela por tamanho
(2RLE, 1RL, SRL, RND, RDO…) e avisa quando algo passa da linha vermelha — antes de gastar etiqueta.
O kit completo — e o motivo de cada peça existir
Texto
Tamanho em pontos, negrito, itálico, alinhamento, cor, espaçamento entre letras, altura de linha e “ajustar” (encolhe até caber na caixa). Parece básico — mas é o que evita etiqueta com texto estourando.
Códigos
CODE128, EAN-13, EAN-8, UPC, CODE39, ITF14, MSI, Pharmacode e QR com correção L/M/Q/H. O app valida o dado: se o número não fecha o dígito verificador do EAN, ele avisa em vez de sair um risco torto.
Imagem e formas
Arrastar imagem, colar com ⌘V, cortar regiões de uma foto para virar objeto, retângulo, elipse, linha, seta, cruz e moldura da etiqueta. A moldura faz parte da arte (sai no PNG e na impressão).
Camadas, máscara e cópias
Ordem das camadas, esconder, duplicar, máscara (recorta um objeto usando outro como forma) e “Array…” para repetir o mesmo objeto em grade — de etiqueta de patrimônio a cartela de 20 unidades.
Modelos que sobrevivem a atualização
Modelo salvo no navegador é frágil: limpar cache, trocar de janela, atualizar o app — some. Aqui o modelo vai para
arquivo, em dados/modelos.json ao lado do app, com uma cópia da versão anterior em
modelos.bak.json. Se a gravação falhar no meio, o backup salva o dia. E o backup/restauração em JSON existe
para levar modelos de uma máquina para outra sem sobrescrever: modelo repetido entra como “(importado)”.
40 etiquetas diferentes em um clique (e sem digitar nada de novo)
O motivo de o app existir, na prática: casa de bombas com 40 equipamentos. Cada etiqueta tem TAG, nome e setor. Digitar 40 vezes é garantia de erro de digitação — e erro em etiqueta de segurança não é detalhe.
O fluxo é este: você monta uma etiqueta com marcadores {{codigo}}, {{nome}} e
{{setor}} (ou liga cada camada a uma coluna pelo painel Campos da etiqueta), escolhe o arquivo
.xlsx ou .csv, confere a prévia da primeira etiqueta e manda imprimir. O app lê a planilha
dentro da máquina — nada é enviado para a internet.
- Ligar sozinho: o app compara o nome da camada com o nome da coluna e já sugere o vínculo. Palpite conservador de propósito: “Setor B” não vira “setor” por engano.
- Baixar planilha do layout: gera um
.xlsxcom uma coluna por camada da etiqueta, já com exemplos — acabou o “como é que eu monto a planilha?”. - Vias por linha: coluna
copias/qtdfaz a mesma etiqueta sair várias vezes. - Relatório do lote: ao terminar, dá para baixar um
.txtcom o que saiu, o que falhou e por quê.
Quando o app ficou “de verdade” — e quebrou de um jeito estranho
Abrir no Chrome em modo app funciona, mas tem cara de site. A virada foi criar uma janela nativa
(WKWebView) gerada na hora com osacompile: ícone no Dock, título, foco, restaurar ao clicar no ícone.
O visual segue sendo o mesmo HTML — só que dentro de uma janela de app.
Só que aí apareceu o bug mais instrutivo do projeto. No app instalado, três botões não faziam nada:
- Escolher planilha (.xlsx / .csv)
- Baixar modelo .xlsx
- Baixar planilha do layout
Nenhum erro, nenhum aviso. Clicava e o silêncio. A causa: um WebView não tem seletor de arquivo nem download por padrão. Abrir aquela janelinha do Finder e salvar arquivo são coisas que o app precisa implementar — o Chrome faz, o WebView puro não faz. E o app não tinha esse pedaço.
A gambiarra que virou arquitetura
Como o app já roda um servidor Perl local (é ele que serve o HTML e conversa com a impressora), a solução foi ensinar esse servidor a abrir os diálogos do macOS:
GET /pick→ abre “escolher arquivo”, devolve o arquivo escolhido em base64;POST /save→ abre “salvar como”, grava e revela no Finder; se o diálogo falhar, salva direto em~/Downloads;- o HTML ganhou uma “ponte” que só liga quando percebe que está no WebView — no Chrome continua tudo como sempre foi.
QR ou código de barras? A resposta com número, não com achismo
Pergunta legítima de quem vai imprimir: “cabe isso no código?”. Em vez de repetir o que dizem por aí, medi nas bibliotecas que o app realmente usa — e conferi decodificando de volta.
Resumo da medição: um QR de 1.000 caracteres ocupa 105 × 105 módulos; de 2.000, 149 × 149; o teto de 2.953 chega em 177 × 177. No papel, com a térmica a 203 dpi, o mínimo confiável é 2 pontos por módulo (0,25 mm). Numa área de 44 mm isso dá 2,6 pontos/módulo para 1.000 caracteres — passa — e 1,9 para o teto, que sai borrado. Ou seja: até ~1 KB é seguro no lado curto; o teto só cabe no lado longo de 101 mm.
Detalhe técnico: imagem dentro de QR (dá, mas é pouco)
zbarimg. Funciona e volta
byte a byte idêntico — mas o base64 infla 33%, e o teto do QR é 2.953 bytes, então cabe imagem de no máximo
~2,1 KB. Na prática: ícone de 16 px (108 B) vira QR 45 × 45; foto de 64 × 64 (1 KB) vira QR 125 × 125 e ainda decodifica.
Foto de verdade, não. Para etiqueta, URL curta ou um ID resolve melhor — e quem lê precisa de um app que entenda base64,
porque o leitor comum só mostra texto.
Botão “Nova etiqueta” — e a versão que finalmente diz a verdade
Um pedido simples: começar do zero sem apagar objeto por objeto. Agora existe o botão azul
Nova etiqueta no topo do painel esquerdo, antes do seletor de papel. Ele pergunta antes, diz o que vai
apagar, e não mexe no que não deve: papel escolhido e calibração ficam, modelos salvos ficam. Deu errado?
Ctrl/⌘ Z volta.
Junto veio um detalhe que parecia cosmético e não era: o número da versão no cabeçalho estava digitado à mão
no HTML (v1.46.0) enquanto o pacote já ia na 1.50. Ou seja: o app mentia sobre a própria versão — e é justamente
esse número que responde “você está com o build novo?”. Agora o número é injetado no build a partir de um único lugar, e o
empacotador aborta se não achar o marcador. Nunca mais dessincroniza.
Lacre de segurança: a palavra repetida que esconde outra
O pedido veio como imagem: uma etiqueta coberta por LACRE em diagonal, e no meio dela, de vez em quando, GARANTIA aparecendo em outro estilo. É o velho truque de segurança: difícil de copiar, fácil de reconhecer de longe — se a etiqueta foi violada, o padrão denuncia.
O modificador virou um item de texto normal com uma chave ligada: Padrão diagonal. Você digita a palavra, opcionalmente a palavra de destaque, e regula quatro coisas:
- % — chance do destaque aparecer (10% é discreto, 15% é o “lacre clássico”);
- entr — espaçamento entre linhas (1,35 denso, 1,75 arejado);
- âng — inclinação (−33° é o padrão que parece carimbo);
- cobrir etiqueta — estica a caixa exatamente na área imprimível.
Detalhe técnico: por que o padrão não “embaralha” entre prévia e impressão
Do zip ao papel em oito passos
Coloque em ~/freeLabel ou numa pasta sua. Downloads/Desktop/Documents fazem o macOS bloquear a execução dos scripts.
Se aparecer “desenvolvedor não identificado”: clique com o botão direito → Abrir → Abrir. Só na primeira vez.
Tem que aparecer freeLabel v1.52.0 e o botão azul Imprimir SLP650 (sem “script não encontrado”).
No rolo padrão: SLP-SRL / MPL4 — 54 × 101. O app mostra o tamanho SII (SRLDPI) logo abaixo.
Texto, código de barras, QR, imagem ou um modelo da galeria. Arraste com o mouse, ajuste fino com as setas, Shift = 0,1 mm.
“Imprimir teste de escala” gera moldura + quadrado de 20 × 20 mm. Meça com régua: 20 mm = está 1:1.
Azul = direto na SLP650 (sem driver e sem senha). “Imprimir (diálogo)” é o caminho antigo, só com fila instalada — nele use 100%, nunca “ajustar à página”.
Dê um nome, escolha o grupo e salve. Fica em arquivo na pasta do app, com backup, e aparece na galeria para reutilizar.
A arquitetura que ninguém vê (e que explica os bugs)
Para quem gosta do encanamento — e para quem for mexer no app depois:
Peças
index.html— o app inteiro: layout, canvas, libs embutidasserver.pl— servidor local (Perl puro) em127.0.0.1:8731window.jxa— a janela nativa (JXA + WKWebView)2-imprimir.command— PNG → filtrorastertosiislp→ USBdados/modelos.json— modelos, com.bak
Rotas do servidor
/print— enfileira um job (fila de verdade, um por vez)/job,/queue— estado e resultado de cada etiqueta/tpl— grava/lê modelos (com cópia de segurança)/pick,/save— diálogos do macOS (WebView não tem)/health,/log— diagnóstico em uma tela
Detalhe técnico: como a impressão realmente acontece
POST /print. O servidor grava o arquivo em /tmp, enfileira, e um processo filho chama
2-imprimir.command com arquivo, tamanho SII e cópias. O retorno (incluindo a saída do filtro) volta para a
tela e para o log. É por isso que dá para saber se o problema foi conversão, papel ou cabo — em vez de só “não imprimiu”.
Detalhe técnico: por que tem tanta checagem de sintaxe e de runtime
node --check no script e depois abre a página num Chrome
headless procurando um marcador data-js-error que o próprio app preenche se algo estourar. Se o HTML
quebrar, o build falha antes de virar zip. Isso já pegou erro bobo mais de uma vez.
Detalhe técnico: as bibliotecas e o “sem internet”
Três dias: do 1.0 ao 1.52
Filtro oficial da SII, backend USB, scripts numerados e o primeiro PNG saindo na SLP650.
HTML único, medidas em milímetros, presets de rolo, texto/código/QR/imagem. Primeiras versões do “freeLabel designer”.
Modelos saem do navegador e viram arquivo com backup; planilha entra no jogo, com mapeamento de colunas e relatório.
WKWebView via osacompile, ATS liberado para o servidor local, ícone e foco de app de verdade.
Descoberta de que WebView não abre arquivo nem baixa: pontes /pick e /save no servidor Perl.
Botão com confirmação e desfazer; versão do cabeçalho passa a vir do build, não digitada à mão.
Modificador de texto que repete a palavra e esconde a segunda, com semente fixa e cobertura exata da área imprimível.
O que o app ainda não faz (e a gente sabe)
- Sincronizar entre dispositivos: modelo salvo fica na máquina. Para levar, use backup/restauração em JSON
- Registro de porto/equipamento: o app imprime bem; ele não é banco de dados de ativos (ainda)
- Leitor de código: ele gera QR e código de barras, não lê — conferência é no leitor/sistema de quem usa
- Windows: é macOS. O caminho de impressão depende do filtro CUPS e do backend USB do sistema
- Etiqueta contínua longa: o limite prático é o tamanho de mídia que o driver da SLP650 conhece
Cada item desses é uma decisão adiada de propósito: enquanto não aparecer necessidade real, mexer nisso só aumenta a chance de quebrar o que já funciona. Esse app é usado para imprimir etiqueta de segurança em campo — “bonitinho” perde para “não me deixa errar”.
Resumo em três linhas
Um app de arquivo único, offline, que desenha etiqueta em milímetros e imprime numa SLP650 sem instalar nada. Tem lote por planilha, modelos em arquivo, códigos validados e padrão de segurança. Roda numa janela nativa — e, quando o sistema não coopera, o servidor local dá um jeito.