Technical Article

Códigos de barras en PDF con Delphi: QR, PDF417, DataMatrix

Un código de barras en una etiqueta de envío o en una factura tiene una sola función, que es ser leído por un escáner en la primera pasada. Si sobrevive a esa pasada se decide mucho antes de que el paquete llegue al muelle. Se decide por la forma en que se colocó el símbolo en la página. El error más común en un flujo de informes en Delphi es renderizar el código de barras como un mapa de bits en otro lugar y colocar esa imagen en el PDF. Se ve bien en pantalla con un nivel de zoom y luego se degrada en todos los demás lugares

La alternativa es dibujar el símbolo como contenido vectorial, directamente en la página. PDFlibPas expone una familia de llamadas de dibujo exactamente para esto, que cubre los símbolos de matriz 2D QR, PDF417 y DataMatrix, las familias lineales a través de Code128 y GS1-128, y USPS Intelligent Mail para automatización postal. El argumento a favor del vector no es estético. Se trata de si las barras caen donde el escáner las espera

Por qué el formato vectorial supera a un mapa de bits colocado

Un código de barras es un patrón de barras y espacios, o en dos dimensiones una cuadrícula de módulos oscuros y claros. El decodificador funciona midiendo la relación de esos anchos. Cualquier cosa que distorsione las relaciones es ruido que consume el margen de error del símbolo. Una imagen de código de barras rasterizada tiene píxeles fijos. Cuando el PDF se procesa en una impresora cuyos puntos no se dividen uniformemente en la cuadrícula de la imagen, el rasterizador tiene que volver a muestrear, y los bordes de los módulos que deberían ser nítidos se extienden a través de dos píxeles del dispositivo. Una barra estrecha puede ensancharse, un espacio adyacente puede estrecharse y la relación de ancho en la que confía el decodificador se desvía

Dibujado como contenido vectorial, el mismo símbolo es un conjunto de rectángulos rellenos descritos en coordenadas de espacio de usuario de PDF. No hay una cuadrícula de píxeles fija con la que luchar. Al momento de la impresión, el dispositivo procesa cada rectángulo a la resolución que realmente tiene, por lo que cada borde del módulo es tan nítido como lo permite el hardware, a cualquier escala y tamaño de impresión. Escale un símbolo vectorial para una etiqueta de palet o redúzcalo para un paquete y la geometría se mantendrá exacta. Esa precisión es lo que mantiene alta la tasa de lectura en la primera pasada, que es el objetivo de colocar un código de barras en la página

Códigos QR y los cuatro niveles de corrección

QR es un símbolo de matriz 2D que se lee en ambos ejes a la vez, por lo que empaqueta una gran cantidad de datos en un cuadrado pequeño. Su tolerancia al daño proviene de la corrección de errores Reed-Solomon, que se ofrece en cuatro niveles. El nivel L recupera aproximadamente el 7 por ciento de las palabras de código, el M aproximadamente el 15 por ciento, el Q aproximadamente el 25 por ciento y el H aproximadamente el 30 por ciento. La corrección más alta no es gratuita. Las palabras de código de recuperación ocupan la capacidad del módulo, por lo que, para una cantidad fija de datos, un nivel más alto obliga a un símbolo más denso o físicamente más grande

La elección es una cuestión sobre el entorno en el que vivirá el símbolo. Un documento digital limpio que solo se escaneará desde una pantalla puede permanecer en L y ser compacto. Una etiqueta que se imprimirá, manipulará, rozará y posiblemente se cubrirá parcialmente con cinta adhesiva requiere Q o H, porque la redundancia adicional es lo que permite a un decodificador reconstruir la carga útil a partir de un símbolo que ya no está impecable. DrawQRCode toma la posición y un SymbolSize que fija el ancho y el alto dibujados, más un valor de EncodeOptions que selecciona el modo de datos (0 para automático, o variantes numéricas, alfanuméricas, ISO-8859-1 y UTF-8) y un valor de DrawOptions para la orientación

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;

El nivel de corrección en sí es elegido por el codificador para ajustar los datos en el símbolo que solicitó. Si necesita un nivel alto garantizado para un entorno difícil, dimensione el símbolo generosamente para que el codificador tenga el presupuesto de módulos para gastar en redundancia en lugar de verse obligado a reducirlo para que quepa

PDF417 para identificaciones y etiquetas de envío

PDF417 es un símbolo lineal apilado. Cada fila es un código de barras lineal corto, y las filas se apilan para formar un bloque, razón por la cual aparece en licencias de conducir, tarjetas de embarque y etiquetas de envío de transportistas donde una franja más amplia de datos debe ubicarse en un espacio rectangular. Su corrección de errores funciona en una escala de 0 a 8. Cada paso duplica aproximadamente el número de palabras de código de corrección, por lo que el nivel 5 conlleva mucha más redundancia que el nivel 1, a costa de más palabras de código en la página

La forma de un bloque PDF417 es ajustable, y eso importa porque la etiqueta tiene un área fija que llenar. DrawPDF417SymbolEx expone los controles que la llamada básica no ofrece. FixedColumns y FixedRows fijan el recuento de columnas de datos y de filas, y el valor 0 indica que el codificador decida. ErrorLevel toma -1 para automático o un valor explícito de 0 a 8. ModuleSize es el ancho del elemento más estrecho en la unidad de medida actual, y HeightWidthRatio establece la altura de cada módulo en relación con su ancho, que es cómo se hace el bloque corto y ancho o alto y estrecho para que coincida con el espacio que tiene

// 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

Fijar las columnas es la palanca habitual en una plantilla de etiquetas. Un recuento de columnas constante le da al bloque un ancho predecible, por lo que el diseño circundante no se desplaza a medida que la carga útil codificada cambia de longitud de un documento al siguiente, mientras que el codificador añade filas hacia abajo para absorber la diferencia

DataMatrix para marcas pequeñas

DataMatrix es el símbolo a elegir cuando la marca tiene que ser pequeña. Es una cuadrícula 2D compacta que utiliza ECC 200, el esquema Reed-Solomon moderno, y sigue siendo legible en tamaños en los que un símbolo QR con los mismos datos resultaría incómodo. Eso lo convierte en la opción estándar para el marcado directo de piezas, componentes electrónicos pequeños y etiquetas de logística densas

DrawDataMatrixSymbol toma un ModuleSize para el paso de punto, un Encoding de 1 para ASCII y un SymbolSize que es 0 para automático o una de las dimensiones cuadradas y rectangulares estándar, desde 10x10 hasta 132x132. El parámetro Options combina la orientación con el ancho de la zona silenciosa, donde añadir de 100 a 400 establece un borde blanco de uno a cuatro módulos. La zona silenciosa no es decorativa. Un decodificador necesita ese margen despejado para encontrar el patrón de localización del símbolo, y un símbolo apretado contra otra tinta es un símbolo que no se logra adquirir

// 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

Dónde siguen dominando los códigos de barras 1D

Los símbolos bidimensionales llaman la atención, pero los códigos de barras lineales todavía poseen gran parte del comercio minorista y la logística, y la razón es la base instalada de escáneres láser que leen un solo barrido. Code128 es el caballo de batalla para datos alfanuméricos, y su eficiencia proviene de tres conjuntos de caracteres. El conjunto A cubre caracteres de control y mayúsculas, el conjunto B cubre el rango ASCII imprimible completo y el conjunto C es el que importa para los números. El subconjunto C codifica un par de dígitos en un solo carácter de símbolo, por lo que una serie de datos numéricos ocupa la mitad de los caracteres de símbolo que ocuparía en el conjunto A o B. Esa es la forma más compacta de colocar un código de barras numérico largo, y la implementación de Code128 de PDFlibPas combina automáticamente los conjuntos B y C para lograrlo

GS1-128, el estándar anteriormente denominado EAN-128, basa en Code128 al llevar identificadores de aplicación, los prefijos entre corchetes que le indican a un sistema receptor si los dígitos siguientes son un número de serie, un código de lote o una fecha de caducidad. La estructura está marcada por FNC1, un carácter especial que no es de datos que indica que el símbolo está codificado según GS1 y separa los campos de longitud variable. En PDFlibPas, dibuja un símbolo GS1-128 con DrawBarcode utilizando el tipo Code128 y el marcador literal [FNC1] colocado en la cadena de datos donde comienza cada identificador de aplicación

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 el correo, USPS Intelligent Mail, también llamado OneCode, codifica datos de enrutamiento y seguimiento en un solo código de barras modulado en altura para la automatización postal. DrawIntelligentMailBarcode toma geometría explícita para el ancho de la barra, la altura completa de la barra, la altura del localizador y el ancho del espacio, con los datos suministrados como una cadena de dígitos de solo 20, 25, 29 o 31 dígitos. Las alturas explícitas de la barra y del localizador existen porque el símbolo lleva información sobre si cada barra es una barra completa, un ascendente o un descendente, y el lector postal depende de que esas alturas se mantengan según las especificaciones

Dibujo en la página y medición para el diseño

Cada llamada que se muestra aquí dibuja en el contenido de la página seleccionada actualmente, la misma superficie que recibe el texto y las imágenes, por lo que un código de barras se produce como parte de la generación normal del documento en lugar de importarse como un recurso independiente. Debido a que los símbolos son contenido vectorial, los datos que codifican y la geometría que ocupan se conocen al momento de dibujar, lo que le permite colocarlos de manera determinista

El diseño de las familias lineales se beneficia de realizar mediciones primero. GetBarcodeWidth devuelve el ancho total dibujado de un código de barras para un ancho de barra estrecho y tipo de código de barras dados, de modo que pueda reservar el espacio horizontal exacto antes de comprometer el dibujo, en lugar de adivinar y descubrir una superposición después de construir la página. Los símbolos 2D son más sencillos de colocar porque establece su tamaño dibujado directamente a través de SymbolSize o ModuleSize, y el símbolo llena ese espacio. En cualquier caso, la disciplina es la misma. Decida el tamaño físico a partir del entorno de escaneo, confirme que el símbolo encaja en el espacio que tiene y deje que la geometría vectorial mantenga cada borde nítido desde la vista previa en pantalla hasta la impresión final

Para el flujo de trabajo de construcción de páginas más amplio en el que se integran estos códigos de barras, las técnicas en nuestro artículo sobre extracción de texto, imágenes y fuentes cubren la lectura de contenido de un PDF, y la guía para fusionar y dividir PDF de gran tamaño con acceso directo muestra cómo ensamblar documentos de gran volumen de manera eficiente. Ambos se complementan de manera natural con la API de dibujo descrita aquí, que se distribuye como parte de la Delphi PDF Library para Delphi y C++Builder junto con las API de texto, gráficos, formularios y firmas cubiertas en otras secciones de este blog