El archivo abre sin problemas en su máquina. Acrobat lo muestra, la vista previa de impresión se ve bien, todas las páginas están ahí. Después llega a la imprenta, o al sistema de archivo que ingiere su lote mensual, y vuelve rechazado: imágenes RGB en un trabajo CMYK, sin clave /Trapped, un output intent que no coincide con la prensa. Nada estaba mal en el documento que alguien pudiera ver. Estaba mal frente a un perfil, y el perfil se revisó en un lugar donde usted no estaba. Preflight es el nombre que la preimpresión le da a esa revisión, y la pregunta de fondo es dónde corresponde hacerla cuando los PDF salen de su propio código Delphi y no del escritorio de un diseñador
HotPDF no le entrega una función de preflight para llamar. El componente lleva una ventana de informe de preflight en su demo con interfaz gráfica, pero detrás no hay ninguna API que un servicio o un script de compilación pueda invocar, y fingir lo contrario lo mandaría a buscar un método que no existe. Eso suena a hueco hasta que usted nota que, para archivos que genera usted mismo, llamar a un validador sobre su propia salida tiene de todos modos la forma equivocada. Usted ya controla cada propiedad que un validador inspeccionaría. La división útil consiste en volver al generador incapaz de emitir un archivo malo, y luego demostrarlo con una herramienta que usted no escribió
Por qué su propia salida se revisa de otra manera
El preflight tradicional supone el archivo de un desconocido. Algún diseñador, alguna otra aplicación, alguna cadena ignota de ediciones lo produjo, y usted lo inspecciona porque no tiene idea de qué hay adentro. Un documento que produjo su código no es un desconocido. La incrustación de fuentes, el espacio de color, el output intent, el bloque de metadatos: su programa decidió todo eso unos milisegundos antes de que el archivo llegara al disco. Inspeccionarlo después para descubrir decisiones que usted acaba de tomar es trabajo inútil. La jugada más barata es restringir esas decisiones para que un archivo no conforme jamás llegue a existir
También hay una razón de credibilidad para mantener la verificación afuera. Una biblioteca que bendice su propia salida se está corrigiendo su propio examen. Cuando el sistema de archivo de un cliente o el RIP de una imprenta rechaza su archivo, decir que su componente asegura que está bien no pesa nada. Un veredicto de veraPDF o de Acrobat sí pesa, porque la otra parte corre esas mismas herramientas
Haga de la conformidad una configuración, no una lista de control
La capa de prevención es pura configuración. Establezca PDFACompliance o PDFXCompliance antes de BeginDoc y HotPDF sostiene las reglas correspondientes durante toda la pasada de generación: incrusta las fuentes, vigila el uso de DeviceRGB y DeviceCMYK frente al output intent que usted declaró, y rechaza las funciones que el perfil prohíbe. Las contradicciones afloran en EndDoc, donde las puertas de conformidad lanzan un error en lugar de despachar en silencio algo que fallará más adelante. Una vez guardado el archivo, esas mismas propiedades devuelven lo que realmente se aplicó, que es el dato que más necesita la bitácora de su pipeline:
// Después de EndDoc: registre los perfiles aplicados junto a los metadatos de la corrida
if Pdf.PDFACompliance <> '' then
Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);
Ponga esos indicadores en la misma línea de bitácora que el hash de los datos de entrada y la versión de HotPDF. El día en que un validador y su generador discrepen sobre un archivo, esa línea le dirá qué plantilla lo produjo y qué compilación de la biblioteca estaba cargada, y la discusión que de otro modo se comería una tarde se vuelve un grep. Los output intents, los perfiles ICC y el etiquetado que están detrás de estos indicadores se detallan en la guía de salida PDF/A, PDF/X y PDF/UA con HotPDF
Una primera puerta barata para archivos que usted no generó
No todo pipeline es puramente generativo. Los clientes suben PDF, los escáneres los dejan en una carpeta, los socios los adjuntan al correo. Empujar cada uno de esos archivos por un validador estructural completo desperdicia tiempo de cola en archivos que ni siquiera abrirán. La Direct File API de HotPDF lee lo suficiente de la estructura de un archivo para responder si es un PDF utilizable siquiera, sin cargar todo el árbol de objetos, lo que la convierte en un buen lugar para fallar rápido:
function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
Handle, Pages: Integer;
begin
Result := False;
Handle := Pdf.DAOpenFileReadOnly(FileName, '');
if Handle <= 0 then
Exit; // estructuralmente ilegible: en cuarentena, no validar
try
Pages := Pdf.DAGetPageCount(Handle);
Result := Pages > 0;
finally
Pdf.DACloseFile(Handle);
end;
end;
Dos hechos sobre esta API deciden cómo la envuelve usted. El atajo de memoria plana solo vale para entradas sin cifrar; si le pasa una contraseña a DAOpenFileReadOnly, la llamada recae en silencio en un análisis completo, así que un archivo que usted sabe cifrado debería pasar por DecryptFile hacia una copia de trabajo limpia antes del triaje. Y DAGetPageCount no significa nada sobre un handle que no abrió bien, así que la comprobación del handle se mantiene estricta y un resultado no positivo es un rechazo, no un reintento. Hay más patrones de este tipo en el artículo sobre la Direct File API para flujos con PDF grandes
veraPDF, ejecutado como parte de la compilación
Para todo lo que usted declare como PDF/A o PDF/UA, veraPDF es el validador que conviene integrar. Corre sin interfaz, acepta un lote, emite XML o JSON y nombra cada falla por su cláusula ISO, de modo que una regla incumplida contra la cláusula 6.2.2 de ISO 19005-1 apunta directo a un ajuste del generador en vez de dejarlo a usted adivinando. Manejarlo desde Delphi es simple control de procesos:
function RunVeraPdf(const PdfFile, ReportFile: string): Cardinal;
var
Cmd: string;
SI: TStartupInfo;
PI: TProcessInformation;
begin
Cmd := Format('cmd /c verapdf.bat --format xml "%s" > "%s"',
[PdfFile, ReportFile]);
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
if not CreateProcess(nil, PChar(Cmd), nil, nil, False,
CREATE_NO_WINDOW, nil, nil, SI, PI) then
RaiseLastOSError;
try
WaitForSingleObject(PI.hProcess, 120000); // acote la espera por archivo
GetExitCodeProcess(PI.hProcess, Result);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;
Ese tiempo límite se gana su lugar. Un archivo malformado puede meter a cualquier analizador en un rincón del que nunca sale, y una espera sin final dentro de un proceso de cola arrastra consigo al resto de la cola. Acote la espera, dele al vencimiento su propio código de falla y aparte el archivo para que lo mire una persona. Cuando lea el resultado, analice el XML buscando identificadores de regla y no el texto legible. Los identificadores de regla sobreviven a las actualizaciones del validador; la redacción de los mensajes no, y un código estable es algo que un ingeniero de soporte puede buscar en tiquetes antiguos
Cómo corre el lote importa tanto como si cada archivo pasa. Un proceso por archivo, no uno por lote, para que una entrada venenosa le cueste el tiempo límite de ese archivo y nada más. Limite la cantidad de procesos del validador al número de núcleos, porque armar el informe XML depende de la CPU y sobresuscribir solo genera atropello. Y ponga un techo de tamaño en la recepción, porque un libro escaneado de dos gigabytes se adueñará de la cola por muy paciente que sea el analizador. Nada de eso es preflight en sentido estricto. Es la diferencia entre una puerta que sobrevive al volumen de cierre de mes y otra que se apaga la primera noche que atasca el pipeline a las 2 de la madrugada
PDF/X es donde esto se queda corto. veraPDF no lo valida, así que la comprobación práctica sigue siendo el Preflight de Acrobat con el perfil ISO 15930 que le indicó su impresor. Acrobat exige una persona, lo que implica muestreo en lugar de cobertura total: el primer archivo salido de una plantilla nueva, más un pequeño sorteo aleatorio de cada lote, mientras la puerta automatizada se encarga de todo lo que puede resolverse sin nadie. Una revisión por muestreo que de verdad se ejecuta le gana a una automatización completa que queda a medias para siempre
Un informe que seguirá queriendo dentro de un año
Una puerta de preflight rinde dos veces. Una cuando detiene un archivo malo en la entrada, y otra mucho después, cuando alguien pregunta por qué se dejó pasar cierto archivo. Ese segundo momento es el que debería dictar el formato, porque es el momento en que un informe pobre lo deja a usted varado. Por cada archivo revisado, guarde el hash de entrada, los indicadores de conformidad del generador y la versión de la biblioteca que salen de la línea de bitácora anterior, el nombre y la versión del validador, el perfil contra el que se revisó, el resultado de aprobación o falla, y los identificadores de las reglas incumplidas con sus números de página siempre que el validador los entregue. Guarde ese informe junto al archivo que describe. Póngalo en un sistema aparte y ese sistema será dado de baja antes que el archivo histórico que documenta
Las excepciones también hay que dejarlas por escrito. Cuando un cliente insiste en despachar un archivo que a la puerta no le gusta, la respuesta no es aflojar la regla para todos. Registre quién aprobó ese archivo, con qué fundamento y hasta qué fecha, y adjunte esa dispensa a su informe. Una dispensa con nombre y vencimiento es una decisión que alguien asume. Una revisión comentada de forma temporal es un incidente esperando su fecha
Un hábito más se paga solo: cuando un archivo falla, cópielo a una carpeta de regresión con nombre antes de que nadie lo toque. Casi todo problema de preflight que valga la pena depurar se remonta a una entrada específica, y los equipos que conservan esas entradas arreglan la recurrencia en una hora en lugar de esperar a que reaparezca en producción. Las propiedades de conformidad y la Direct File API mostradas aquí forman parte del HotPDF Delphi Component para Delphi y C++Builder, cuya documentación cubre cada llamada en detalle