Artículo técnico

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 un solo trabajo, 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 a la rampa de carga. Se decide por cómo se colocó el símbolo en la página. El error más común en una canalización de reportes de 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 a un nivel de zoom y luego se degrada en todas partes

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, cubriendo 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 para el formato vectorial no es estético. Se trata de si las barras caen donde el escáner espera que estén

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 proporción de esos anchos. Cualquier cosa que distorsione las proporciones es ruido que consume el presupuesto de errores del símbolo. Una imagen de código de barras rasterizada conlleva píxeles fijos. Cuando el PDF se renderiza en una impresora cuyos puntos no se dividen uniformemente en la cuadrícula de la imagen, el rasterizador tiene que remuestrear, 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 engordar, un espacio adyacente puede adelgazarse, y la proporción de ancho en la que se basa 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 cual pelear. En el momento de la impresión, el dispositivo renderiza cada rectángulo a la resolución que realmente tiene, por lo que cada borde de módulo es tan nítido como el hardware lo permita, a cualquier escala y a cualquier tamaño de impresión. Amplíe un símbolo vectorial para una etiqueta de palet o redúzcalo para un paquete y la geometría se mantiene exacta. Esa precisión es lo que mantiene alta la tasa de lectura en la primera pasada, que es todo el punto de poner 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 leído en ambos ejes a la vez, lo que explica por qué empaqueta mucha información en un cuadrado pequeño. Su tolerancia al daño proviene de la corrección de errores Reed-Solomon, ofrecida en cuatro niveles. El nivel L recupera aproximadamente el 7 por ciento de las palabras clave, el M alrededor del 15 por ciento, el Q alrededor del 25 por ciento y el H alrededor del 30 por ciento. La corrección superior no es gratuita. Las palabras clave de recuperación ocupan capacidad de los módulos, 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 decisión es una cuestión sobre el entorno en el que vivirá el símbolo. Un documento digital limpio que solo será escaneado desde una pantalla puede quedarse en L y mantenerse compacto. Una etiqueta que será impresa, manipulada, raspada y posiblemente cubierta en parte por cinta adhesiva necesita Q o H, porque la redundancia adicional es lo que le permite a un decodificador reconstruir la carga útil a partir de un símbolo que ya no está prístino. DrawQRCode toma la posición y un SymbolSize que fija el ancho y alto dibujados, más un valor de EncodeOptions que selecciona el modo de datos (0 para automático, o variantes numérico, alfanumérico, 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 = milímetros
    Pdf.NewPage;

    // QR cuadrado de 30 mm, codificación automática, orientación normal
    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 que los datos quepan 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 forzado a bajar para encajar

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, que es la razón por la que aparece en licencias de conducir, pases de abordar y etiquetas de envío de transportistas donde una tira más ancha de datos tiene que 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 clave de corrección, de modo que el nivel 5 conlleva mucha más redundancia que el nivel 1, a costa de más palabras clave 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 tiene. FixedColumns y FixedRows fijan el conteo de columnas de datos y el conteo de filas, donde 0 significa dejar 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 qué tan alto es cada módulo relativo a su ancho, que es cómo se hace el bloque corto y ancho o alto y angosto para coincidir con el espacio que tiene

// Fijo 10 columnas de datos, filas automáticas, nivel de error 5,
// módulo de 0.30 mm de ancho, filas de tres veces el ancho del módulo de alto
Pdf.DrawPDF417SymbolEx(20, 60, 'PDF417 PAYLOAD 0123456789',
  0,        // Options: 0 = orientación normal
  10,       // FixedColumns
  0,        // FixedRows: 0 = automático
  5,        // ErrorLevel: 0 a 8
  0.30,     // ModuleSize, en la unidad de medida actual
  3.0);     // HeightWidthRatio

Fijar columnas es la palanca habitual en una plantilla de etiqueta. Un conteo 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 a otro, mientras el codificador agrega 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 usa ECC 200, el esquema moderno Reed-Solomon, y sigue siendo legible a tamaños donde un símbolo QR con los mismos datos serí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 tamaño del punto, una codificación (Encoding) de 1 para ASCII, y un tamaño de símbolo (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 agregar de 100 a 400 establece un borde blanco de uno a cuatro módulos. La zona silenciosa no es decoración. Un decodificador necesita ese margen libre para encontrar el patrón de búsqueda del símbolo, y un símbolo apiñado contra otra tinta es un símbolo que no se logra adquirir

// DataMatrix ASCII con tamaño automático, módulo de 0.5 mm, orientación normal
// con una zona silenciosa de un módulo (Options 0 + 100)
Pdf.DrawDataMatrixSymbol(20, 110, 0.5, 'DMX-SN-4408812',
  1,        // Encoding: 1 = ASCII
  0,        // SymbolSize: 0 = automático
  100);     // Options: normal + zona silenciosa de un módulo

Dónde los códigos de barras 1D todavía dominan

Los símbolos bidimensionales reciben la atención, pero los códigos de barras lineales todavía dominan grandes partes del comercio minorista y la logística, y la razón es la base instalada de escáneres láser que leen en 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 completo de ASCII imprimible, 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 del símbolo, por lo que una corrida de datos numéricos toma la mitad de los caracteres del símbolo que tomaría en el conjunto A o B. Esa es la manera más compacta de colocar un código de barras numérico largo, y la implementación de Code128 en PDFlibPas combina los conjuntos B y C automáticamente para lograrlo

GS1-128, el estándar anteriormente llamado EAN-128, se basa en Code128 al incluir Identificadores de Aplicación (Application Identifiers), los prefijos entre corchetes que le dicen al 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 marca el símbolo como codificado en GS1 y separa los campos de longitud variable. En PDFlibPas se dibuja un símbolo GS1-128 con DrawBarcode usando 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, con los marcadores FNC1 esto se convierte en un símbolo GS1-128.
  // AI 21 (serie) = ABC123, AI 20 (variante) = 13
  Pdf.DrawBarcode(20, 150, 60, 18, '[FNC1]21ABC123[FNC1]2013',
    3,        // Barcode: 3 = Code128
    0);       // Options: 0 = dibujo predeterminado

  // Medir el ancho renderizado para una barra estrecha de 0.30 mm antes del diseño
  W := Pdf.GetBarcodeWidth(0.30, '[FNC1]21ABC123[FNC1]2013', 3);
end;

Para 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 automatización postal. DrawIntelligentMailBarcode toma geometría explícita para el ancho de la barra, la altura de la barra completa, la altura del rastreador y el ancho del espacio, con los datos suministrados como una cadena de 20, 25, 29 o 31 dígitos solamente. Las alturas explícitas de la barra y del rastreador existen porque el símbolo contiene información en si cada barra es una barra completa, un ascendente o un descendente, y el lector postal depende de que esas alturas se mantengan de acuerdo con las especificaciones

Dibujar en la página y medir para el diseño

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

El diseño para las familias lineales se beneficia de medir primero. GetBarcodeWidth devuelve el ancho total dibujado de un código de barras para un ancho de barra estrecha y un tipo de código de barras determinados, para 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 simples de colocar porque establece su tamaño dibujado directamente a través de SymbolSize o ModuleSize, y el símbolo llena ese espacio. De cualquier manera, 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 más amplio de construcción de páginas en el que caen 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 unir y dividir PDF grandes con acceso directo muestra cómo ensamblar documentos de alto volumen de manera eficiente. Ambos combinan naturalmente con la API de dibujo descrita aquí, que se incluye 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 partes de este blog