Artigo Técnico

Gerar Códigos de Barras Líveis em PDF com HotPDF em Delphi

Um código de barras não é uma imagem com que se decora um documento. É uma medição, e o leitor é o instrumento que a lê. Esse reenquadramento decide quase tudo sobre como se deve desenhar um código de barras num PDF. As barras não transportam informação pela sua negrura; a informação vive na razão entre as larguras das barras e as larguras dos espaços, e um leitor recupera-a cronometrando as transições à medida que um laser ou sensor percorre a superfície. Comprima essa geometria, torne-a desfocada ou aperte as suas margens, e produz-se algo que parece exatamente um código de barras mas é lido como uma mancha. O HotPDF oferece duas formas de colocar um numa página, e a diferença entre elas é precisamente a diferença entre controlar essa geometria e abdicar dela

Página PDF apresentando uma grelha de códigos de barras lineares desenhados pelo HotPDF em várias simbologias
Simbologias de códigos de barras lineares desenhadas num único PDF com o HotPDF

O que o HotPDF consegue codificar

O HotPDF desenha simbologias lineares (unidimensionais), e o conjunto é mais vasto do que a maioria dos projetos precisa. A enumeração THPDFBarcodeType cobre a família Code 2 of 5 nas suas formas intercalada, industrial e matricial; o Code 39 e a sua variante estendida; os três subconjuntos A, B e C do Code 128; o Code 93 simples e estendido; MSI; PostNet; Codabar; o grupo retalhista UPC e EAN, nomeadamente EAN-8, EAN-13, UPC-A, os comprimidos UPC-E0 e UPC-E1, e os complementos suplementares UPC de 2 e 5 dígitos; e os subconjuntos GS1-128 (EAN-128). Isso é suficiente para cobrir etiquetas de cadeia de abastecimento, pontos de venda a retalho e os códigos industriais mais antigos ainda em uso em armazéns

O que não desenha é a família bidimensional. Não há aqui QR, Data Matrix ou PDF417. Esses codificam bytes numa grelha com matemática de correção de erros própria, e se um requisito nomear um deles, esta é a ferramenta errada, e convém sabê-lo antes de construir a solução em torno dela, não depois. Para códigos unidimensionais, a questão prática é mais restrita: qual a simbologia que aceita os dados que realmente se têm, porque as codificações não são intermutáveis

As restrições de dados são reais e mordem no momento da geração. As variantes Code 2 of 5 e o MSI aceitam apenas dígitos. O Code 39 transporta letras maiúsculas, dígitos e um punhado de sinais de pontuação; se forem precisas minúsculas ou o intervalo ASCII completo, isso é Code 39 Extended ou um subconjunto do Code 128. O Code 128C empacota dois dígitos em cada símbolo por densidade, pelo que exige uma string numérica de comprimento par e nada mais. O EAN-13 espera doze dígitos e calcula o décimo terceiro como verificação; o EAN-8 espera sete e calcula o oitavo; o UPC-A aceita doze. Dê a uma simbologia dados que ela não consiga representar e não obtém uma exceção útil, obtém um código de barras que codifica lixo, o que é pior, porque parece correto até alguém o ler numa caixa registadora

Dois caminhos de desenho, dois níveis de controlo

O método a usar em produção é DrawBarcode, no objeto de página. Recebe a simbologia, uma posição, uma altura e um parâmetro que importa mais do que todos os outros: MUnit, a largura do módulo. O módulo é a largura da barra mais estreita, o átomo do qual toda a outra medida no código é um múltiplo, e é expresso aqui em pontos. Tudo sobre se o resultado impresso é lido corretamente remonta a esse único número inteiro

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'label.pdf';
    Pdf.BeginDoc;

    // BCType, X, Y, Height, MUnit (largura do módulo em pontos), ângulo,
    // data, UseCheckSum, cor das barras, cor de fundo.
    Pdf.CurrentPage.DrawBarcode(
      bcCodeEAN13,           // simbologia
      72, 680,               // X, Y em pontos a partir do canto inferior esquerdo
      60,                    // altura das barras
      1,                     // MUnit: barra mais estreita de 1pt
      0,                     // sem rotação
      '123456789012',        // 12 dígitos; o 13.º é o dígito de verificação
      True,                  // acrescenta o dígito de verificação módulo 10
      clBlack, clWhite);     // barras a preto, fundo a branco

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Dois argumentos merecem uma análise mais atenta. UseCheckSum acrescenta o dígito de verificação módulo 10 que a simbologia espera, e para os códigos de retalho quase sempre se quer True; desligue-o apenas quando os dados já trouxerem uma verificação pré-calculada, ou obterá um dígito duplicado. As cores das barras e do fundo são os dois últimos parâmetros, e a tentação de ser criativo aí é uma armadilha discutida adiante. Note também a origem das coordenadas: como em qualquer outra chamada de desenho no HotPDF, X e Y são medidos a partir do canto inferior esquerdo da página em pontos, com Y a crescer para cima, a mesma convenção percorrida no exemplo Hello World

O segundo caminho é DirectDrawBarcode, que recebe os dados e uma caixa delimitadora, X, Y, Width, Height, e escala o padrão de barras para preencher essa largura. É prático para dispor códigos numa grelha, porque se indica o espaço ocupado e o método ajusta as barras a ele. Essa conveniência é também o seu perigo. Ao indicar uma largura, deixa de se estar a definir o tamanho do módulo; o método divide o espaço disponibilizado pelo número de barras que os dados exigem, e a barra mais estreita passa a ser o que resultar dessa divisão. Peça uma string densa em Code 128 dentro de uma caixa demasiado estreita e os módulos encolhem abaixo do que qualquer leitor consegue resolver, silenciosamente. Para tudo o que tenha de ser lido de forma fiável, prefira DrawBarcode e defina MUnit deliberadamente. Reserve DirectDrawBarcode para pré-visualizações e para disposições em que já se mediu que as barras resultantes se mantêm legíveis

A largura do módulo é uma decisão de resolução

Eis a aritmética que determina se a etiqueta funciona. Um leitor a laser e uma câmara têm ambos uma menor característica que conseguem distinguir, e a barra estreita tem de ficar confortavelmente acima desse limite depois da impressão. O limiar amplamente citado para códigos lineares de uso geral é uma barra estreita de 13 mil, cerca de 0,33 mm, e muitos guias de retalho e industriais tratam isso como um mínimo, não como um alvo. Traduzindo para unidades PDF: um ponto equivale a 1/72 de polegada, aproximadamente 0,353 mm, pelo que um único ponto de largura de módulo fica mesmo nesse limiar. É por isso que MUnit := 1 é o menor valor em que se deve confiar para um código destinado a um leitor real, e por que duplicá-lo para 2 compra uma margem que quase nada custa numa etiqueta com espaço de sobra

Agora ligue isso à resolução de saída, porque o módulo também tem de sobreviver à impressora. Numa impressora laser de 300 DPI, um ponto de dispositivo é 1/300 de polegada, pelo que um módulo de um ponto tem cerca de quatro pontos de largura. Quatro pontos mal chega para produzir uma margem limpa; a dispersão do toner e um ligeiro erro de registo comem parte disso, e a barra que media um ponto no PDF sai impressa mais grossa ou mais fina do que a especificação permite. Aumente o módulo para 2 pontos e passa a ter oito pontos para trabalhar, o que absorve esse ruído. A regra a interiorizar: a largura de módulo definida em pontos tem de mapear para um número inteiro e confortável de pontos de dispositivo na resolução de impressão real, não na resolução que gostaria de ter. Um código que é lido sem falhas a partir do ecrã e falha na impressora do armazém quase sempre falhou nesta verificação

A zona de silêncio faz parte do símbolo

A razão mais comum para um código de barras corretamente codificado não ser lido é a zona de silêncio, a margem em branco de cada lado das barras. Os leitores usam esse vazio para encontrar onde o código começa e termina; sem ele, o leitor não consegue distinguir a primeira barra do que quer que esteja ao lado na página. As normas são específicas. A maioria das simbologias lineares quer uma zona de silêncio de pelo menos dez vezes a largura do módulo de cada lado, e os códigos de retalho UPC e EAN exigem nove módulos à esquerda e sete à direita. Com um módulo de um ponto, isso são aproximadamente dez pontos, cerca de um sétimo de polegada, de espaço em branco garantido a ladear as barras

O HotPDF desenha as barras e nada mais. Não reserva a zona de silêncio automaticamente, o que significa que a responsabilidade é do programador, e é fácil esquecer. O modo de falha é subtil: coloca-se um código de barras rente ao limite de uma célula de tabela, ou deixa-se o layout da página apertar um logótipo ao lado, e o código que passou todos os testes numa página em branco deixa de ser lido assim que é enviado dentro de um documento real. Reserve a margem explicitamente. Antes de chamar DrawBarcode, deixe pelo menos dez larguras de módulo de espaço livre em ambos os lados, e trate qualquer gráfico, régua ou texto que invada essa faixa como um defeito, não como uma escolha estética

Cor, contraste e a linha legível por humanos

As cores das barras e do fundo existem para se poder combinar com uma paleta de marca, e são a forma mais rápida de partir um código que funciona. Os leitores leem contraste, classicamente com luz vermelha, e esperam barras escuras sobre um fundo claro. Preto sobre branco é a única combinação a usar sem testar. Azul-escuro ou verde-escuro sobre branco pode passar; qualquer combinação com baixo contraste de luminância, e em particular barras vermelhas, que um leitor de luz vermelha vê como fundo, não passará. Se um designer pedir códigos de barras coloridos, a resposta honesta é que as barras ficam a preto e a cor vai para outro sítio na etiqueta

O caminho DrawBarcode também consegue renderizar o texto legível por humanos por baixo das barras, os dígitos que um funcionário digita quando uma leitura falha. Esse texto é um recurso de recurso, não decoração, pelo que, ao colocar a sua própria legenda, mantenha-a fora da zona de silêncio; um rótulo da simbologia espremido na margem lateral anula o mesmo espaço em branco de que o leitor depende. Os campos no exemplo aqui, incluindo TextOut para quaisquer legendas envolventes, são as mesmas chamadas de desenho abordadas no guia de saída de relatórios, que é o sítio a consultar quando o código de barras é um elemento numa página composta maior

Um breve hábito de verificação

As barras vetoriais são uma vantagem que vale a pena nomear. Como DrawBarcode escreve o código como operadores de desenho PDF em vez de uma imagem rasterizada, as barras mantêm-se nítidas em qualquer nível de zoom e o ficheiro não carrega resolução própria; a única resolução que importa é a da impressora. Isso não dispensa os testes, apenas significa que o teste tem de acontecer em papel. Gere uma amostra, imprima-a no dispositivo de menor resolução que os seus códigos realmente vão encontrar, e leia-a com a mesma classe de leitor que os seus utilizadores têm nas mãos, não o leitor de imagem topo de gama na sua secretária. Verifique as zonas de silêncio com uma régua na impressão, confirme que a largura do módulo sobreviveu à viagem de pontos para pontos de dispositivo, e confirme que o valor descodificado corresponde ao que foi codificado, dígito de verificação incluído. Cinco minutos com um leitor real apanham todas as falhas descritas acima, e apanham-nas antes de uma palete de stock mal etiquetado o fazer

Os métodos DrawBarcode e DirectDrawBarcode aqui mostrados fazem parte do HotPDF Delphi Component para Delphi e C++Builder