Una herramienta de verificación previa por lotes (preflight) es un programa de consola sin interfaz gráfica, dirigido a una carpeta de archivos PDF, que valida cada uno de ellos frente a los estándares de conformidad especificados y genera una prueba legible por máquina de sus hallazgos. Nadie se sienta a observarla: se ejecuta a las dos de la mañana mediante una tarea programada (cron) o el Programador de tareas de Windows, o como un filtro en un flujo de integración continua (CI), y la siguiente entidad interesada en su resultado será un planificador que lee un código de salida o un auditor que abre un reporte semanas después. Esto cambia la definición de lo "correcto". El motor de verificación previa del PDFium Component, una biblioteca PDF con código fuente para Delphi, C++Builder y Lazarus, hace que las llamadas de validación en sí sean muy sencillas. El trabajo que determina si la herramienta cumple su propósito se encuentra alrededor de esas llamadas: qué perfil se comprobó, qué indicó el código de salida al planificador y si el reporte que habría detectado un error sigue existiendo cuando alguien decide buscarlo
El contrato: lo que un planificador realmente puede ver
Un ejecutor de CI o el Programador de tareas de Windows ven exactamente dos cosas de su herramienta: el código de salida y los archivos que generó. Las líneas de registro, los colores de la consola y la salida de progreso están pensados para un operador en vivo, pero a las dos de la mañana no hay nadie. Por lo tanto, defina claramente el comportamiento del código de salida antes de modificar la API:
0: todos los archivos cumplen con cada perfil solicitado1: al menos un archivo generó hallazgos de validación2: la herramienta falló en al menos un archivo (entrada corrupta, bloqueo, error del sistema)
La distinción entre los códigos 1 y 2 es la que muchos desarrolladores omiten y luego lamentan. Un PDF dañado que no se abre no es un fallo de validación. Si lo agrupa en el código 1, una gran cantidad de escaneos dañados se mostrará en sus paneles de control como una caída repentina de la conformidad, obligando a alguien a rastrear una regresión de estándares que nunca existió, cuando el problema real es un escáner defectuoso en origen
Otros dos elementos pertenecen al contrato. El primero es un límite de tiempo (timeout) por archivo. Un PDF problemático, con miles de páginas y estructuras de objetos profundamente anidadas, puede detener un paso de validación durante minutos, y una ventana de procesamiento nocturno no puede esperar. Cancele el proceso de ese archivo al cumplirse el tiempo límite, regístrelo como un fallo de la herramienta y continúe con el lote. El segundo es un directorio de cuarentena: traslade a un lado cada entrada que haya superado el tiempo límite o que no se pueda abrir, en lugar de dejarla en su sitio. En unos meses, ese directorio acumulará los peores documentos que sus clientes reales envían, y ese conjunto de archivos tiene más valor para las pruebas de lanzamiento que cualquier ejemplo sintético que pueda crear manualmente
Seleccionar estándares y por qué el nivel de conformidad es clave
La enumeración TPdfPreflightStandard cubre las familias de uso habitual: ppsPdfA para conformidad de archivo ISO 19005, ppsPdfUa para accesibilidad ISO 14289, ppsPdfX para intercambio de impresión, además de ppsPdfE, ppsPdfR y ppsPdfVT para ingeniería, rasterizado y datos variables. Dentro de una familia, el motor lee el nivel de conformidad que declara el documento y lo reporta por estándar en el ConformanceName del resultado. Limitarse a nombrar la familia rara vez es suficiente, ya que el nivel es donde reside la diferencia real. PDF/A-2b promete reproducibilidad visual y nada más. PDF/A-3a añade la exigencia de etiquetado de estructura lógica y permite archivos de origen incrustados, lo cual representa un límite mucho más difícil de superar para material escaneado que carece de árbol de etiquetas. Si configura esto de forma incorrecta, el procesamiento por lotes le entregará información errónea. Si su política de retención requiere PDF/A-2b pero rechaza archivos por falta de etiquetas de estructura, el reporte se llenará de hallazgos que nadie corregirá. Aceptar cualquier etiqueta PDF/A sin verificar el nivel significa aprobar documentos que cumplen con una norma inferior a la acordada. Las normativas de accesibilidad exigen cada vez más PDF/UA sobre todo lo anterior, lo cual no añade costo a la ejecución debido a que BuildPdfPreflightReport (de la unidad FPdfPreflightReport) toma un conjunto de estándares:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Una sola llamada evalúa ambos estándares y devuelve un único registro de reporte consolidado
Por qué una lista de hallazgos vacía no es una aprobación
El reporte enumerates los hallazgos por estándar, y una lista de problemas vacía significa únicamente "no se encontraron problemas en los estándares que realmente se ejecutaron". Esta es una afirmación mucho más limitada que "el archivo cumple con el estándar que a usted le interesa", y la brecha entre ambas es donde la verificación previa por lotes falla en silencio. Un error de escritura en la configuración que omita ppsPdfA del conjunto produce exactamente la misma lista de problemas vacía que un archivo realmente correcto. Por lo tanto, considere sospechoso el silencio. Recorra Report.Results y verifique dos cosas para cada estándar que deseaba comprobar: que exista una entrada de resultado y que su bandera IsCompliant, respaldada por Status = pfsPass, sea verdadera. Una tarea nocturna que asocia "sin hallazgos" con "listo para archivar" sin confirmar qué estándares se evaluaron es la forma típica en que una carpeta de archivos no conformes pasa los controles durante meses, hasta que un auditor externo abre uno con veraPDF y se cuestiona todo el archivo
Una segunda trampa se oculta en la propia definición de un hallazgo. Cada TPdfPreflightIssue contiene un Code, una Category, una Description y una Recommendation, y señala la regla infringida, no una página o un objeto. Esa es una decisión de diseño con consecuencias en el ciclo de retroalimentación: el reporte indica al equipo de producción qué clase de defecto existe (como una fuente no incrustada o un identificador XMP ausente), y la tarea de localizar el objeto específico del error corresponde a la herramienta de corrección en un paso posterior, no al validador. Desarrolle sus consumidores de reportes basándose en los valores estables de Code, nunca en el texto de descripción, el cual puede cambiar de redacción entre versiones sin previo aviso
Archivos de reporte para máquinas y para el personal de soporte
El registro de reporte escribe los mismos hallazgos en cinco formatos: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile y SaveMarkdownToFile, cada uno con su respectiva función del tipo ToJson si prefiere la cadena de texto en memoria antes que en el disco. Evite quedarse con uno solo. Escriba JSON para el flujo de trabajo, de modo que la CI pueda adjuntarlo al registro del proceso y analizar los códigos de problemas y estados sin procesar texto. Escriba HTML para el personal técnico, ya que se abre en cualquier navegador sin herramientas adicionales. Ambas opciones juntas requieren solo una línea de código adicional por archivo y le evitan al ingeniero de guardia la tarea más frustrante en el procesamiento por lotes: desglosar un bloque JSON sin procesar a las dos de la mañana para saber qué archivo falló. Un criterio es clave más allá del formato: derive el nombre de cada reporte a partir del nombre del archivo de entrada, nunca de una marca de tiempo; de lo contrario, dos ejecuciones paralelas entrelazarán reportes que ya no podrá asociar con sus entradas
Los límites de gravedad corresponden a la configuración y no al código. Una anotación con no descripción alternativa es un fallo grave para un portal de entrega PDF/UA y una nota prescindible para un archivo interno; sin embargo, el hallazgo es idéntico en ambos. Exponga un nivel de rechazo por perfil para que las políticas puedan cambiar sin necesidad de recompilar, y registre el nivel que estaba activo en el propio resumen del proceso. El próximo trimestre nadie recordará bajo qué límite se ejecutó el lote del pasado octubre, y el resumen es el único lugar donde se conserva esa información
Aislar archivos para que un PDF defectuoso no detenga el lote
procedure RunPreflightBatch(const InputDir, ReportDir: string;
out FilesWithFindings, ToolFailures: Integer);
var
SR: TSearchRec;
Pdf: TPdf;
Report: TPdfPreflightReport;
begin
FilesWithFindings := 0;
ToolFailures := 0;
if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
try
repeat
Pdf := TPdf.Create(nil); // fresh instance per file: no state bleed
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // load failures are silent, not raised
raise EPdfError.Create('Cannot open ' + SR.Name);
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
if Report.TotalIssueCount > 0 then
Inc(FilesWithFindings);
except
on E: Exception do
begin
Inc(ToolFailures); // exit-code-2 territory, not a validation verdict
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
Tres decisiones deliberadas definen ese bucle. Un objeto TPdf nuevo por archivo garantiza que un documento que corrompa el estado del motor no afecte a los archivos siguientes. La comprobación explícita de Active es necesaria porque Active := True descarta los errores de carga en lugar de generarlos; si omite ese control, un archivo incompleto avanzará hasta la llamada de validación antes de fallar más adelante con un mensaje confuso. El bloque interno try..except se mantiene en el ámbito de cada archivo intencionalmente, para que una sola excepción incremente el contador de fallos y el bucle continúe. Usted necesita reportes limpios para los 4,999 archivos correctos incluso si el archivo 5,000 está dañado. Además, ambos formatos de reporte se escriben en el disco antes de determinar el veredicto, lo que significa que la evidencia se conserva incluso si falla la lógica de conteo en el resumen posterior
La asignación del código de salida se reduce a unas pocas líneas en el archivo de proyecto:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// falling through exits with 0: every file conformed
end.
Lo que la verificación previa no hará por usted
El motor detecta, no repara. Un hallazgo sobre una fuente no incrustada o un espacio de color dependiente del dispositivo es una orden de trabajo para quien genere los archivos, y el validador no tiene forma de corregirlo. Por lo tanto, planifique el ciclo de retroalimentación cuidadosamente. Los reportes deben llegar a donde el equipo de producción pueda leerlos; de lo contrario, los mismos errores reaparecerán cada noche hasta que alguien pregunte por qué la tasa de conformidad no mejora. También es útil contrastar una muestra de veredictos con un validador independiente (veraPDF para PDF/A o la verificación previa de Acrobat para PDF/X) antes de que un auditor externo lo haga. Cuando dos motores no coinciden en un archivo de un cliente real, ese documento no es una molestia: es exactamente el caso de regresión que faltaba en sus pruebas de lanzamiento. Consérvelo, asígnele un nombre y ejecútelo en cada compilación
Vale la pena conocer otra combinación. El mismo motor de validación gestiona las comprobaciones interactivas en una interfaz de usuario de revisión, de modo que este CLI sin entorno gráfico y una plataforma de recepción y revisión de PDF orientada al análisis pueden compartir el mismo vocabulario de validación sin desincronizarse con el tiempo. Y debido a que [ppsPdfA, ppsPdfUa] evalúa la accesibilidad en el mismo paso, la sección PDF/UA del lote se alinea directamente con el trabajo del visor como construir un lector de PDF accesible en Delphi. Los perfiles, formatos de reporte y la API completa de verificación previa están documentados en la página del producto para el PDFium Component