Um código de barras em uma etiqueta de envio ou fatura tem uma única função, que é ser lido por um scanner logo na primeira tentativa. Se ele sobreviverá a essa leitura é algo decidido muito antes de a encomenda chegar à doca. É decidido pela forma como o símbolo foi inserido na página. O erro mais comum em um fluxo de relatórios no Delphi é renderizar o código de barras como um bitmap em outro local e inserir essa imagem no PDF. Fica bom na tela em um determinado nível de zoom, mas perde qualidade em todos os outros lugares.
A alternativa é desenhar o símbolo como conteúdo vetorial, diretamente na página. O PDFlibPas expõe uma família de chamadas de desenho exatamente para isso, cobrindo os símbolos de matriz 2D QR, PDF417 e DataMatrix, as famílias lineares por meio do Code128 e GS1-128, e o USPS Intelligent Mail para automação postal. O argumento para o uso de vetores não é estético. Trata-se de garantir que as barras fiquem exatamente onde o leitor espera que estejam.
Por que o vetor vence um bitmap posicionado
Um código de barras é um padrão de barras e espaços ou, em duas dimensões, uma grade de módulos escuros e claros. O decodificador funciona medindo a proporção dessas larguras. Qualquer coisa que distorça as proporções representa ruído que consome a tolerância de erro do símbolo. Uma imagem rasterizada de código de barras contém pixels fixos. Quando o PDF é renderizado em uma impressora cujos pontos não se dividem uniformemente na grade da imagem, o rasterizador precisa reamostrar, e as bordas dos módulos que deveriam ser nítidas acabam espalhadas por dois pixels do dispositivo. Uma barra estreita pode alargar, um espaço adjacente pode afinar, e a proporção de largura na qual o decodificador confia acaba sofrendo desvios.
Desenhado como conteúdo vetorial, o mesmo símbolo é um conjunto de retângulos preenchidos descritos em coordenadas de espaço do usuário do PDF. Não há uma grade de pixels fixa com a qual lutar. No momento da impressão, o dispositivo renderiza cada retângulo na resolução que ele realmente possui, de forma que cada borda de módulo seja tão nítida quanto o hardware permite, em qualquer escala e tamanho de impressão. Aumente a escala de um símbolo vetorial para uma etiqueta de palete ou reduza-a para um pacote, e a geometria permanecerá exata. Essa precisão é o que mantém alta a taxa de leitura na primeira tentativa, o que constitui todo o propósito de inserir um código de barras na página.
QR codes e os quatro níveis de correção
O QR é um símbolo de matriz 2D lido em ambos os eixos simultaneamente, e é por isso que ele agrupa muitos dados em um quadrado pequeno. Sua tolerância a danos provém da correção de erros Reed-Solomon, oferecida em quatro níveis. O Nível L recupera cerca de 7 por cento dos codewords, o M cerca de 15 por cento, o Q cerca de 25 por cento e o H cerca de 30 por cento. Uma correção maior não é gratuita. Os codewords de recuperação ocupam a capacidade dos módulos, de modo que, para uma quantidade fixa de dados, um nível mais alto exige um símbolo mais denso ou fisicamente maior.
Essa escolha depende do ambiente em que o símbolo existirá. Um documento digital limpo que só será lido a partir de uma tela pode permanecer no nível L e se manter compacto. Uma etiqueta que será impressa, manuseada, desgastada e possivelmente coberta em parte por fita adesiva precisa dos níveis Q ou H, pois a redundância extra é o que permite ao decodificador reconstruir a carga de dados a partir de um símbolo que não está mais intacto. O DrawQRCode recebe a posição e um SymbolSize que estabelece a largura e a altura desenhadas, além de um valor EncodeOptions que seleciona o modo de dados (0 para automático, ou variantes numéricas, alfanuméricas, ISO-8859-1 e UTF-8) e um valor DrawOptions para a orientação.
var
Pdf: TPDFlib;
begin
Pdf := TPDFlib.Create(nil);
try
Pdf.NewDocument;
Pdf.SetPageSize('A4');
Pdf.SetMeasurementUnits(1); // 1 = millimetres
Pdf.NewPage;
// 30 mm square QR, automatic encoding, normal orientation
Pdf.DrawQRCode(20, 20, 30, 'https://www.loslab.com/', 0, 0);
Pdf.SaveToFile('Label_QR.pdf');
finally
Pdf.Free;
end;
end;
O próprio nível de correção é escolhido pelo codificador para ajustar os dados ao símbolo que você solicitou. Se você precisar de um nível alto garantido para um ambiente adverso, dimensione o símbolo generosamente para que o codificador tenha margem de módulos para gastar em redundância, em vez de ser forçado a reduzir a qualidade para se ajustar ao espaço.
PDF417 para documentos de identificação e etiquetas de envio
O PDF417 é um símbolo linear empilhado. Cada linha é um pequeno código de barras linear, e as linhas se empilham para formar um bloco, motivo pelo qual ele aparece em carteiras de motorista, cartões de embarque e etiquetas de envio de transportadoras onde uma faixa mais larga de dados deve se acomodar em um espaço retangular. Sua correção de erros opera em uma escala de 0 a 8. Cada etapa dobra aproximadamente o número de codewords de correção, de modo que o nível 5 carrega muito mais redundância do que o nível 1, ao custo de mais de codewords na página.
O formato de um bloco PDF417 é ajustável, e isso é importante porque a etiqueta tag uma área fixa a ser preenchida. O DrawPDF417SymbolEx expõe os controles que a chamada básica não possui. O FixedColumns e o FixedRows fixam a contagem de colunas e linhas de dados, com 0 indicando que o codificador deve decidir. O ErrorLevel recebe -1 para automático ou um valor explícito de 0 a 8. O ModuleSize é a largura do elemento mais estreito na unidade de medida atual, e o HeightWidthRatio define a altura de cada módulo em relação à sua largura, permitindo que você torne o bloco baixo e largo ou alto e estreito para corresponder ao espaço disponível.
// Fixed 10 data columns, automatic rows, error level 5,
// module 0.30 mm wide, rows three times the module width tall
Pdf.DrawPDF417SymbolEx(20, 60, 'PDF417 PAYLOAD 0123456789',
0, // Options: 0 = normal orientation
10, // FixedColumns
0, // FixedRows: 0 = automatic
5, // ErrorLevel: 0 to 8
0.30, // ModuleSize, in the current measurement unit
3.0); // HeightWidthRatio
Fixar colunas é a prática comum em um modelo de etiqueta. Uma contagem constante de colunas confere ao bloco uma largura previsível, de forma que o layout circundante não se altere conforme a carga de dados codificada muda de tamanho de um documento para o outro, enquanto o codificador adiciona linhas para baixo para absorver a diferença.
DataMatrix para marcações pequenas
O DataMatrix é o símbolo ideal para quando a marcação precisa ser pequena. Trata-se de uma grade 2D compacta que usa o ECC 200, o esquema moderno Reed-Solomon, e permanece legível em tamanhos nos quais um símbolo QR com os mesmos dados seria inviável. Isso o torna a escolha padrão para marcação direta de peças, pequenos componentes eletrônicos e etiquetas logísticas densas.
O DrawDataMatrixSymbol recebe um ModuleSize para o espaçamento dos pontos (dot pitch), uma Encoding de 1 para ASCII e um SymbolSize que é 0 para automático ou uma das dimensões quadradas e retangulares padrão, de 10x10 a 132x132. O parâmetro Options combina a orientação com a largura da zona de silêncio (quiet-zone), onde adicionar de 100 a 400 define uma borda branca de um a quatro módulos. A zona de silêncio não é enfeite. Um decodificador precisa dessa margem limpa para encontrar o padrão localizador do símbolo, e um símbolo espremido contra outros elementos gráficos é um símbolo que falha ao ser detectado.
// Auto-sized ASCII DataMatrix, 0.5 mm module, normal orientation
// with a one-module quiet zone (Options 0 + 100)
Pdf.DrawDataMatrixSymbol(20, 110, 0.5, 'DMX-SN-4408812',
1, // Encoding: 1 = ASCII
0, // SymbolSize: 0 = automatic
100); // Options: normal + one-module quiet zone
Onde os códigos de barras 1D ainda dominam
Os símbolos bidimensionais recebem bastante atenção, mas os códigos de barras lineares ainda dominam grandes partes do varejo e da logística, e o motivo é a base instalada de leitores a laser que realizam uma única varredura. O Code128 é a ferramenta de trabalho para dados alfanuméricos, e sua eficiência provém de três conjuntos de caracteres. O conjunto A cobre caracteres de controle e letras maiúsculas, o conjunto B cobre toda a faixa ASCII imprimível e o conjunto C é o que importa para números. O subconjunto C codifica um par de dígitos em um único caractere de símbolo, de forma que uma sequência de dados numéricos consome a metade dos caracteres de símbolo que consumiria nos conjuntos A ou B. Essa é a maneira mais compacta de registrar um código de barras numérico longo, e a implementação do Code128 do PDFlibPas combina os conjuntos B e C automaticamente para alcançá-la.
O GS1-128, padrão anteriormente chamado de EAN-128, baseia-se no Code128 ao carregar Identificadores de Aplicação, os prefixos entre colchetes que informam a um sistema receptor se os dígitos seguintes representam um número de série, um código de lote ou uma data de validade. A estrutura é marcada pelo FNC1, um caractere especial de controle (não de dados) que sinaliza o símbolo como codificado em GS1 e separa campos de tamanho variável. No PDFlibPas, você desenha um símbolo GS1-128 com DrawBarcode usando o tipo Code128 e o marcador literal [FNC1] posicionado na string de dados onde cada identificador de aplicação começa.
var
W: Double;
begin
// Code128, with FNC1 markers this becomes a GS1-128 symbol.
// AI 21 (serial) = ABC123, AI 20 (variant) = 13
Pdf.DrawBarcode(20, 150, 60, 18, '[FNC1]21ABC123[FNC1]2013',
3, // Barcode: 3 = Code128
0); // Options: 0 = default drawing
// Measure the rendered width for a 0.30 mm narrow bar before laying out
W := Pdf.GetBarcodeWidth(0.30, '[FNC1]21ABC123[FNC1]2013', 3);
end;
Para correspondências, o USPS Intelligent Mail, também chamado de OneCode, codifica dados de roteamento e rastreamento em um único código de barras modulado por altura para automação postal. O DrawIntelligentMailBarcode recebe dimensões geométricas explícitas para a largura da barra, a altura total da barra, a altura do rastreador (tracker) e a largura do espaço, com os dados fornecidos como uma string de 20, 25, 29 ou 31 dígitos contendo apenas números. As alturas explícitas da barra e do rastreador existem porque o símbolo carrega informações baseadas no fato de cada barra ser uma barra completa, uma ascendente ou uma descendente, e o leitor postal depende de que essas alturas estejam de acordo com a especificação.
Desenhando na página e medindo para o layout
Cada chamada exibida aqui desenha no conteúdo da página selecionada no momento, a mesma superfície que recebe seus textos e imagens, de modo que um código de barras é produzido como parte da geração normal do documento, em vez de ser importado como um ativo separado. Como os símbolos são conteúdo vetorial, os dados que eles codificam e a geometria que ocupam são ambos conhecidos no momento do desenho, o que permite posicioná-los de forma determinística.
O layout das famílias lineares se beneficia de medições prévias. O GetBarcodeWidth retorna a largura total desenhada de um código de barras para uma determinada largura de barra estreita e tipo de código de barras, de modo que você pode reservar o espaço horizontal exato antes de realizar o desenho, em vez de adivinhar e descobrir uma sobreposição após a página ser construída. Os símbolos 2D são mais simples de posicionar porque você define o tamanho desenhado diretamente por meio de SymbolSize ou ModuleSize, e o símbolo preenche esse espaço. De qualquer forma, a disciplina é a mesma. Decida o tamanho físico com base no ambiente de leitura, confirme se o símbolo cabe no espaço disponível e permita que a geometria vetorial mantenha cada borda nítida, desde a visualização na tela até a impressão final.
Para o fluxo geral de construção de páginas no qual esses códigos de barras se inserem, as técnicas em nosso artigo sobre extração de texto, imagem e fonte cobrem a leitura do conteúdo de volta a partir de um PDF, e o guia para mesclagem e divisão de PDFs grandes com acesso direto mostra como montar documentos de alto volume de forma eficiente. Ambos se alinham perfeitamente com a API de desenho descrita aqui, fornecida como parte da Biblioteca Delphi PDF para Delphi e C++Builder, junto com as APIs de texto, gráficos, formulários e assinaturas cobertas em outras partes deste blog.