Una imprenta transaccional te devuelve tu corrida de 80,000 páginas con un rechazo de una sola línea: "no es PDF/VT, el RIP no puede cachear". El archivo se abre bien en cualquier visor de tu equipo, los colores son correctos y los datos se combinaron bien. Nada de eso es lo que pidió la prensa digital. La impresión variable de alta velocidad vive o muere según que la prensa pueda reconocer que el bloque del logotipo del cliente en la página 1 es exactamente el mismo objeto que el de la página 40,000, renderizarlo una vez y reutilizarlo. PDF/VT es el estándar que hace comprobable esa promesa para la máquina, y "se ve correcto" es justo la trampa, porque la estructura que lee el RIP es invisible en pantalla
PDFiumPas expone esa estructura mediante una superficie pequeña en TPdf: SaveAsPdfVT la escribe, ValidatePdfVT la verifica. Este artículo trata sobre lo que realmente colocan en disco y revisan esos dos métodos, dónde ISO 16612-2 es más estricta de lo que parece al principio y qué partes son anclas estructurales honestas en lugar de una verificación previa completa que puedas facturar a un cliente
Qué estandariza PDF/VT y por qué PDF/X va primero
PDF/VT (ISO 16612-2:2010) no es un formato de archivo nuevo. Es una capa de metadatos de optimización montada sobre un archivo PDF/X, y ese orden es decisivo. El estándar define tres niveles de conformidad, pero solo dos nombran un archivo PDF: PDF/VT-1, un documento único e independiente, y PDF/VT-2, un modelo de conjunto de archivos donde las páginas hacen referencia a recursos externos compartidos. El tercer token que puedes ver, PDF/VT-2s, no es un valor a nivel de archivo en absoluto; vive en un encabezado de flujo MIME descrito en el Anexo A. Si encuentras código que escribe GTS_PDFVTVersion = "PDF/VT-2s" en el XMP de un documento, ese código está mal
La regla innegociable para un archivo único es la base PDF/X. ISO 16612-2 §6.2.1 exige que todo archivo PDF/VT-1 también sea un archivo PDF/X-4 válido. El conjunto de archivos de PDF/VT-2, según §6.2.2, debe asentarse en PDF/X-4p, PDF/X-5g o PDF/X-5pg. Por eso un escritor de PDF/VT no puede limitarse a agregar un par de claves identificadoras: tiene que llevar consigo todo el conjunto de marcadores PDF/X-4, lo que implica un OutputIntent, un perfil ICC de destino incrustado, las entradas coincidentes de XMP e Info del documento, un trailer /ID y nada de cifrado. Omite cualquiera de esos elementos y tendrás un archivo que dice ser PDF/VT y falla en cuanto un consumidor conforme verifica la base. PDFiumPas trata la capa PDF/X-4 como parte del guardado de PDF/VT, así que no llamas primero a SaveAsPdfX por separado; el inyector escribe ambas capas en una sola pasada
Escribir un archivo con SaveAsPdfVT
La llamada mínima no necesita nada más que un documento activo, porque TPdfVTSaveOptions.Default proporciona un perfil ICC sRGB integrado y la conformidad pvc1. El guardado se ejecuta internamente en tres pasos: elimina cualquier seguridad, porque inyectar marcadores en texto plano dentro de un flujo de objetos cifrado lo dañaría; enlaza el diccionario Info existente del documento y el trailer /ID al conjunto de marcadores para que los valores XMP e Info coincidan; y después agrega los objetos PDF/X-4 y PDF/VT mediante una actualización incremental
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
if Pdf.LoadFromFile('statements-merged.pdf') then
begin
// Default options: built-in sRGB OutputIntent, PDF/VT-1, synthesised DPart
if Pdf.SaveAsPdfVT('statements-pdfvt.pdf') then
Writeln('PDF/VT-1 written')
else
Writeln('Save failed (document not active?)');
end;
finally
Pdf.Free;
end;
end;
Para una salida real de producción casi siempre querrás reemplazar el OutputIntent con la caracterización de tu prensa, no con el respaldo genérico sRGB. Proporciona los bytes ICC y los identificadores de condición mediante TPdfVTSaveOptions:
var
Pdf: TPdf;
Opt: TPdfVTSaveOptions;
Icc: TBytes;
begin
Pdf := TPdf.Create(nil);
try
Pdf.LoadFromFile('directmail-merged.pdf');
Icc := LoadIccProfile('GRACoL2013_CRPC6.icc'); // your own loader
Opt := TPdfVTSaveOptions.Default;
Opt.Conformance := pvc1; // pvc2 is normalised to pvc1 on write
Opt.IccProfileData := Icc;
Opt.OutputConditionIdentifier := 'CGATS21_CRPC6';
Opt.OutputCondition := 'Commercial print, coated, CRPC6';
Opt.RegistryName := 'http://www.color.org';
Opt.Title := 'Spring 2026 Direct Mail Run';
Opt.Trapped := ptvFalse; // PDF/X Info /Trapped state
Pdf.SaveAsPdfVT('directmail-pdfvt.pdf', Opt);
finally
Pdf.Free;
end;
end;
Un detalle de ese fragmento es una protección deliberada, no una limitación con la que puedas discutir. Establecer Opt.Conformance := pvc2 no produce un archivo PDF/VT-2. El escritor normaliza cualquier solicitud distinta de pvc1 de regreso a pvc1, porque PDF/VT-2 es un formato de conjunto de archivos y un escritor de archivo único que agrega un solo documento de salida físicamente no puede ensamblar el conjunto de recursos externos que exige §6.2.2. El valor pvc2 existe para la ruta de lectura, de modo que ValidatePdfVT pueda reconocer y reportar un documento existente de conjunto de archivos; no es un destino de escritura
El árbol DPart: la estructura que el RIP realmente lee
El corazón de PDF/VT es la jerarquía de Partes de documento (DPart). Es lo que permite a una prensa dividir una corrida larga en registros, agrupar registros en destinatarios o paquetes de correo y adjuntar metadatos de parte de documento para que el equipo aguas abajo pueda enrutar y facturar cada pieza. ISO 16612-2 §6.5 describe el cableado: el catálogo lleva un /DPartRoot, el nodo raíz DPart lleva /DPartRootNode y una /NodeNameList que nombra cada nivel de la jerarquía, los DPart hoja cubren rangos del árbol de páginas y cada página que pertenece a una parte apunta de vuelta a su hoja mediante una entrada /DPart a nivel de página
Cuando tu documento fuente ya contiene una jerarquía utilizable, SaveAsPdfVT la conserva. Cuando no la contiene, el escritor sintetiza una mínima: un solo DPart a nivel de documento que abarca el árbol de páginas actual en orden, con una referencia inversa /DPart agregada a cada objeto de página activo y una /NodeNameList [/Document] de un solo nivel. Sé honesto contigo mismo sobre lo que es ese árbol mínimo. Es un ancla estructural que cumple los requisitos de forma de §6.5; no es metadatos de negocio. No puede inventar destinatarios, límites de piezas de correo ni lotes de productos, porque esa información nunca estuvo en el origen. Si tienes datos por destinatario, se espera que construyas tú mismo un árbol DPart más profundo y extiendas la /NodeNameList para que coincida con los niveles que creas
Validación que va más allá de la presencia de claves
ValidatePdfVT devuelve un registro TPdfVTValidationResult con tres cosas: la conformidad detectada, un conjunto de Issues y un auxiliar IsCompliant que solo es verdadero cuando la conformidad es un nivel real y el conjunto de problemas está vacío. La enumeración de problemas es deliberadamente específica, de modo que un resultado fallido te diga qué cláusula omitiste en lugar de limitarse a "inválido":
var
Pdf: TPdf;
Res: TPdfVTValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.LoadFromFile('statements-pdfvt.pdf');
Res := Pdf.ValidatePdfVT;
if Res.IsCompliant then
Writeln('PDF/VT compliant: ', VTLevelName(Res.Conformance))
else
begin
if pvviMissingDPartRoot in Res.Issues then
Writeln('DPart hierarchy missing or unusable');
if pvviMissingPdfXIdentifier in Res.Issues then
Writeln('PDF/X-4 base identifier absent');
if pvviMissingOutputIntent in Res.Issues then
Writeln('OutputIntent / ICC profile missing');
if pvviEncryptionPresent in Res.Issues then
Writeln('Encrypted - PDF/X forbids this');
end;
finally
Pdf.Free;
end;
end;
Las dos comprobaciones que vale la pena entender a fondo son el emparejamiento de conformidad y el recorrido DPart, porque ambas antes eran demasiado permisivas y se ajustaron para coincidir con la especificación. En el lado del emparejamiento, el validador hace coincidencia exacta, no "cualquier PDF/X sirve": un archivo PDF/VT-1 solo se acepta sobre una base PDF/X-4, y un archivo PDF/VT-2 solo sobre PDF/X-4p, PDF/X-5g o PDF/X-5pg. Un marcador PDF/VT-1 colocado sobre una base PDF/X-1a se reporta, no se deja pasar
El recorrido DPart es donde vive la mayor parte del rigor. No basta con que el catálogo tenga una clave /DPartRoot, porque un objeto vacío fabricado o uno sin vínculos a páginas tampoco puede consumirse. HasValidDPartHierarchy y el recursivo ValidateDPartNode recorren toda la estructura: siguen los vínculos padre, rechazan hijos duplicados y ciclos, exigen que /Start y /DParts se excluyan mutuamente y requieren que los rangos de páginas hoja cubran el árbol de páginas en orden en profundidad, con /DPart de cada página apuntando a la hoja que la contiene. Todas esas fallas internas se reducen al único bit de problema pvviMissingDPartRoot en lugar de expandirse en la enumeración pública, así que trata esa bandera como "la jerarquía DPart es inutilizable", no literalmente "falta la clave raíz"
Tres trampas sintácticas que ahora aplica el validador
Pasadas sucesivas sobre la Tabla 4 de §6.5 sacaron a la luz formas que versiones anteriores aceptaban pero que el estándar no permite. Son justo el tipo de cosa que un árbol DPart armado a mano suele equivocarse, así que vale la pena señalarlas explícitamente:
/DPartses un arreglo de arreglos, no un arreglo plano. Cada elemento del arreglo externo debe ser a su vez un arreglo de referencias indirectas. Un/DParts [9 0 R]plano se rechaza; la forma conforme es/DParts [[9 0 R] [10 0 R]]. Esto impide que una estructura no jerárquica se disfrace de nivel válido/Endsolo marca un rango real de varias páginas. Un DPart hoja puede llevar/Endsolo cuando también tiene/Start, y/Enddebe quedar después de/Starten el orden del árbol de páginas. Un degenerado/Start 3 0 R /End 3 0 Rahora vuelve inutilizable la jerarquía en lugar de leerse como una parte de una sola página- Los nombres de
/NodeNameListdeben sobrevivir al desempaquetado de nombres PDF como NMTOKEN de XML. Un nombre como/Bad#20Namese expande a uno que contiene un espacio, y eso no es un token válido. La implementación hace una comprobación ASCII ligera (letras, dígitos,.,-,_,:, más bytes no ASCII) que detecta espacios en blanco y errores de delimitadores sin rechazar nombres localizados legítimos o específicos de un proveedor
Marcadores XMP: dos formas de escribir la misma propiedad
La identificación PDF/VT vive en XMP bajo el espacio de nombres pdfvtid, específicamente GTS_PDFVTVersion y GTS_PDFVTModDate, junto con los estándares xmp:CreateDate y xmp:ModifyDate. Un matiz que provoca falsos informes de "ausente" en lectores ingenuos es que cualquiera de estos puede serializarse de dos maneras: como texto de elemento (<pdfvtid:GTS_PDFVTVersion>PDF/VT-1</pdfvtid:GTS_PDFVTVersion>) o como un atributo RDF sobre el elemento de descripción. PDFiumPas lee ambas formas, así que un archivo que otra herramienta escribió en estilo de atributo no recibe penalización. También hace cumplir la regla de coherencia de §6.3 de que GTS_PDFVTModDate debe ser igual a xmp:ModifyDate; una discrepancia genera pvviModDateMismatch
Una regla más de la misma cláusula: un valor desconocido de GTS_PDFVTVersion se conserva como pvcUnknown en lugar de volver a pvcNone. Esa distinción importa en la operación. pvcNone significa "no hay marcador PDF/VT en absoluto, es un PDF normal", mientras que pvcUnknown significa "algo selló una versión que este validador no reconoce" (entre ellos el caso PDF/VT-2s). Confundir ambos ocultaría un archivo mal formado dentro del mismo grupo que un documento plano
Dónde termina la garantía
Conviene ser preciso sobre el límite de lo que prometen estos métodos, porque el cumplimiento de impresión variable tiene dinero real en juego. Las comprobaciones DPart y de emparejamiento son validación estructural a nivel de bytes. Confirman que el esqueleto de optimización, los marcadores base de PDF/X-4, el OutputIntent y el XMP están presentes y son coherentes entre sí. No son una verificación previa de contenido a nivel PDF/X-4: no comprueban que cada color esté dentro de la condición de salida declarada, que todas las fuentes estén incrustadas o que no se haya colado ningún caso prohibido de mezcla de transparencias. Para un trabajo que vas a mandar a una imprenta contratada, combina la validación estructural de PDFiumPas con un motor dedicado de verificación previa PDF/X y una impresión de prueba, igual que harías con cualquier otra afirmación de cumplimiento. La capa estructural atrapa las fallas que rompen silenciosamente la caché del RIP; es la mitad de una comprobación completa, no el todo
Si estás integrando estas comprobaciones en un control de entrega más amplio, el mismo enfoque de análisis a nivel de bytes sustenta el otro trabajo de estándares de la biblioteca, incluyendo la validación de flujos de objetos y referencias cruzadas antes de que un archivo llegue siquiera a la verificación previa, y la disciplina de objetos compartidos detrás de sellos de página reutilizables con Form XObjects, que hace que un documento sea apto para RIP desde el principio. Las API de guardado y validación PDF/VT y PDF/X descritas aquí forman parte del componente PDFium VCL para Delphi y C++Builder, cuya página de producto incluye la referencia completa de conformidad