Cuando PDFXCompliance se pone a 'X-1a', HotPDF valida el color del contenido al final mismo del trabajo: EndDoc llama a THotPDF.EnsurePDFX1aContentColorSpaces, que rechaza cualquier contenido DeviceRGB, CalRGB o ICCBased RGB en cualquier sitio de cualquier página y lanza una excepción que nombra el recurso ofensor. El archivo jamás se escribe como un documento PDF/X-1a que la imprenta va a devolver
Esa última parte es la gracia. Un archivo PDF/X-1a con un logo RGB perdido se ve perfectamente bien en pantalla, pasa una revisión relajada, y luego fracasa en el preflight de la imprenta dos días antes de la tirada. Cazarlo en tu build de Delphi, con un mensaje que te dice qué página y qué imagen, sale mucho más barato que enterarte por un operador de preimpresión molesto
¿Por qué PDF/X-1a rechaza el contenido RGB?
PDF/X-1a rechaza RGB porque el formato promete que cada color ya está finalizado para una condición de impresión CMYK concreta. ISO 15930-1:2001 §6.2.3 limita el color de contenido a DeviceCMYK, DeviceGray y colorantes directos, o sea espacios Separation y DeviceN cuyos alternates son CMYK o Gray. El OutputIntent GTS_PDFX incrustado en el archivo nombra esa condición de impresión, por ejemplo offset estucado FOGRA39, y el flujo receptor está en su derecho de mandar los números directos a las planchas sin conversión de color alguna
RGB rompe ese contrato. Un valor RGB describe un color relativo a un monitor o un espacio de trabajo, no una receta de tinta, así que alguien aguas abajo tendría que decidir cómo separarlo, y X-1a existe precisamente para quitarle esa decisión al receptor. ISO 32000-1 §8.6 agrupa los espacios de color PDF en familias device, CIE-based y especiales, y los miembros RGB aparecen en más de una familia: DeviceRGB es un espacio device, CalRGB e ICCBased con tres componentes son CIE-based, y un espacio Indexed o Separation puede esconder una base o alternate RGB debajo. Un checker que solo busca el operador rg se pierde la mayoría. La edición posterior PDF/X-1a:2003 (ISO 15930-4) conserva la misma regla de color; ojo, el valor 'X-1a' de HotPDF escribe la identificación PDF/X-1a:2001 en el paquete XMP
Si tu contenido de verdad es RGB y debe seguir siéndolo, X-1a es el objetivo equivocado. HotPDF acota este check a 'X-1a' solamente, así que 'X-3' y 'X-4', que permiten color ICC-based y calibrado, quedan intactos
Qué comprueba realmente EnsurePDFX1aContentColorSpaces
EnsurePDFX1aContentColorSpaces combina una bandera de dibujo con un recorrido recursivo de los recursos de cada página, y corre el último en la puerta PDF/X. EndDoc primero hace cumplir los requisitos generales PDF/X: Title debe estar puesto, Trapped debe ser 'True', 'False' o 'Unknown', debe existir un OutputIntent GTS_PDFX (EnsurePDFXOutputIntent), y cada página necesita un TrimBox o ArtBox (EnsurePDFXPageBoxes). Solo después de que eso pase corre el check de color
La primera ruta es una bandera de todo el documento. Los setters de color RGB de HotPDF, la incrustación de imágenes bitmap y las entradas de color de anotaciones anotan el uso de DeviceRGB mientras dibujas, lo que cubre operadores inline rg y RG que jamás aparecen en un diccionario de recursos. La segunda ruta recorre los /Resources de cada página e inspecciona:
- Espacios de color nombrados en
/ColorSpace, incluida la abreviaturaRGBde imagen inline como sinónimo de DeviceRGB - El
/ColorSpacede cada Image XObject - Form XObjects y Patterns de teselado, recursando en sus propios
/Resourcesanidados - Shading Patterns y entradas del diccionario
/Shadingde la página, vía el/ColorSpacedel shading - Espacios base Indexed, alternates Separation y DeviceN, y espacios base de Patterns, seguidos hasta una profundidad de anidamiento de 8
Tres cosas se rechazan: DeviceRGB, CalRGB, y espacios ICCBased cuyo stream de perfil declara N = 3. Los diccionarios de recursos compartidos entre Form XObjects y patterns se visitan una vez, así que un conjunto de forms que se referencian mutuamente no manda el recorrido a un bucle. El método es público en THotPDF, pero en uso normal nunca lo llamas tú, porque EndDoc lo hace por ti
Dibujar una página X-1a en Delphi que pasa el check
Un documento X-1a conforme de HotPDF necesita un OutputIntent CMYK, un TrimBox en cada página, y color puesto solo mediante las APIs CMYK, Gray y spot. El esqueleto de abajo usa el perfil FOGRA39; AddPDFXOutputIntent toma el identificador de condición de salida, una cadena de información, el stream del perfil ICC, su número de componentes, y el espacio alternate
var
Pdf: THotPDF;
Profile: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'brochure-x1a.pdf';
Pdf.Title := 'Spring brochure, press edition';
Pdf.Trapped := 'False';
Pdf.PDFXCompliance := 'X-1a';
Pdf.BeginDoc;
Profile := TFileStream.Create('CoatedFOGRA39.icc', fmOpenRead or fmShareDenyWrite);
try
Pdf.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)',
'Coated FOGRA39 offset', Profile, 4, 'DeviceCMYK');
finally
Profile.Free;
end;
Pdf.CurrentPage.SetTrimBox(0, 0, 612, 792); // obligatorio en cada página
Pdf.CurrentPage.SetCMYKFillColor(0, 0.5, 0.8, 0.1);
Pdf.CurrentPage.Rectangle(72, 600, 468, 120);
Pdf.CurrentPage.Fill;
Pdf.CurrentPage.SetGrayFillColor(0.2); // texto DeviceGray
Pdf.CurrentPage.SetFont('Arial', [], 14);
Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
Pdf.EndDoc; // el check de color corre aquí
finally
Pdf.Free;
end;
end;
Cada llamada basada en TColor es una trampa en este contexto. SetRGBFillColor, SetRGBStrokeColor y SetRGBColor toman todas un color GDI y emiten DeviceRGB, así que una rutina helper escrita hace años para informes orientados a pantalla contaminará un trabajo de imprenta en el momento en que se reutilice. Usa SetCMYKFillColor, SetCMYKStrokeColor o SetCMYKColor con componentes en el rango 0 a 1, o los equivalentes Gray
Colores directos y degradados CMYK
Los colorantes directos están permitidos en X-1a mientras su alternate sea CMYK o Gray. RegisterSeparation construye el espacio Separation y devuelve su nombre de recurso, y RegisterAxialGradient elige el espacio de color del shading a partir del número de componentes que le pases, con cuatro componentes significando DeviceCMYK
var
SpotCS, Ramp: AnsiString;
begin
// Tinta directa con una aproximación CMYK como alternate
SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
[0, 1, 0.8, 0.05]);
Pdf.CurrentPage.SetFillColorSpace(SpotCS);
Pdf.CurrentPage.SetFillColor([1.0]); // tinta al 100%
Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
Pdf.CurrentPage.Fill;
// 4 componentes por parada: el shading es DeviceCMYK, no DeviceRGB
Ramp := Pdf.RegisterAxialGradient(72, 300, 540, 300,
[0, 0, 0, 0], [1, 0.6, 0, 0.2]);
Pdf.CurrentPage.SetFillPattern(Ramp);
Pdf.CurrentPage.Rectangle(72, 260, 468, 80);
Pdf.CurrentPage.Fill;
end;
Pásale tres componentes a la misma llamada de degradado y obtienes un shading DeviceRGB, que el recorrido de recursos rechaza bajo el nombre del shading pattern. Lo mismo vale para una Separation registrada con 'DeviceRGB' como alternate: el nombre directo parece inofensivo, pero el recorrido sigue el alternate y encuentra RGB debajo. Cómo se componen esas tintas directas cuando un visor o RIP recurre al alternate queda cubierto en el artículo sobre renderizar colores directos Separation y DeviceN en Delphi
Las imágenes raster necesitan la misma disciplina. HotPDF fija los bitmaps comprimidos con Flate a DeviceRGB, mientras que un JPEG incrustado con icJpeg conserva su propio número de componentes, así que un JPEG CMYK de cuatro componentes o un JPEG en escala de grises van bien y un JPEG RGB corriente no. Convierte el arte al perfil de imprenta en tu pipeline de imagen antes de que llegue al PDF
¿Por qué /DefaultRGB no salva un archivo PDF/X-1a?
/DefaultRGB no ayuda en X-1a porque la regla va de lo que recibe la imprenta, no de si el RGB está caracterizado. Para PDF/A, HotPDF deja que RegisterDefaultColorSpace('DefaultRGB', ...) absorba el uso de DeviceRGB: una vez que un espacio de color Default mapea DeviceRGB a un perfil ICC, el contenido es independiente del dispositivo y el check de consistencia PDF/A lo acepta. Los desarrolladores que han pasado por ese flujo tienden a intentar el mismo arreglo aquí
EnsurePDFX1aContentColorSpaces ignora deliberadamente ese mapeo. Un valor RGB caracterizado sigue siendo RGB, y el OutputIntent X-1a es una condición de impresión CMYK, así que alguien tendría que convertirlo igualmente. Si recuerdas una diferencia entre los dos perfiles de archivo e imprenta, que sea esta: PDF/A pregunta si un color es inequívoco, PDF/X-1a pregunta si ya es tinta
El orden de dibujo, y la configuración de página que no cuenta
Como el check corre en EndDoc sobre contenido ya escrito, el orden en que montes las cosas no importa. El RGB dibujado antes de asignar PDFXCompliance, o antes de registrar el OutputIntent, se caza igualmente. Eso importa para código que construye páginas en rutinas compartidas y decide el perfil de salida tarde, a veces desde un ajuste de usuario elegido cuando medio documento ya existe
Pdf.BeginDoc;
// ... OutputIntent y TrimBox como antes ...
Pdf.CurrentPage.SetRGBFillColor(clRed); // helper legado, emite DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;
Pdf.PDFXCompliance := 'X-1a'; // decidido tras dibujar
Pdf.EndDoc; // aún lanza: DeviceRGB anotado
Una bandera de dibujo tiene una trampa que conviene conocer si construyes un check parecido por tu cuenta. Cada página nueva se inicializa con un estado gráfico negro vía SetRGBColor(0), que emite un par 0 0 0 rg y 0 0 0 RG, y si ese boilerplate de configuración de página pudiera poner la bandera DeviceRGB, todos los documentos parecerían contaminados por RGB, tanto para este check como para el check de consistencia de color de PDF/A. HotPDF guarda y restaura la bandera alrededor de esa inicialización, así que solo cuenta el color que dibujas de verdad
¿Cómo encuentras el contenido RGB cuando EndDoc lanza?
Lee la ubicación al comienzo del mensaje de la excepción, porque te dice cuál de las dos rutas encontró el problema. El mensaje tiene la forma PDFXCompliance 'X-1a': <where> uses <what>, seguido de la referencia ISO 15930-1 y el consejo de convertir a CMYK o Gray o cambiar a X-3 o X-4. El validador lanza una Exception llana en la primera violación, así que iguala por el texto en vez de por una clase de excepción
document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing)significa que se disparó la bandera de dibujo. No hay número de página, porque la bandera es de todo el documento y se comprueba antes del recorrido. Busca en tu código de dibujo llamadasSetRGB*, bitmaps comprimidos con Flate, JPEG RGB y colores de anotaciónTColorpage 3 image XObject /Im2 uses DeviceRGB contentopage 1 shading Pattern /P1 uses DeviceRGB contentviene del recorrido de recursos y apunta a una clave de recurso exactauses an ICCBased RGB colour space (N=3)tras una ubicaciónColorSpace /...es el resultado típico de registrar un perfil sRGB conRegisterICCProfiley seleccionarlo víaSetFillColorSpace, una ruta que la bandera de dibujo no ve- Las ubicaciones anidadas se encadenan, por ejemplo
page 2 Form XObject /Fm1 ColorSpace /CS1, así que puedes seguir el camino desde la página hasta el recurso que lleva el espacio RGB
try
Pdf.EndDoc;
except
on E: Exception do
begin
if Pos('X-1a', E.Message) > 0 then
LogPrepressFailure(Pdf.FileName, E.Message); // tu propio logger
raise;
end;
end;
Como solo se reporta la primera violación, arreglar un trabajo con varias fuentes de RGB es iterativo: arregla, recompila, lee el siguiente mensaje. Para trabajos por lotes conviene loguear el mensaje completo por archivo y llevar una lista corta de ofensores conocidos, como un bitmap de logo o un helper de gráficas que todavía toma TColor
Dónde encaja la puerta RGB en tu preflight
EnsurePDFX1aContentColorSpaces es una puerta RGB específica que corre dentro de tu build, y se empareja con un preflight PDF/X-1a completo en vez de sustituirlo. Cubre las familias RGB, mediante el recorrido de recursos desde los /Resources de cada página y mediante la bandera de dibujo que los setters de color de HotPDF mantienen para operadores inline rg y RG; los espacios de color anidados se siguen hasta profundidad 8 como guarda contra ciclos malformados, y la primera violación se reporta por EndDoc. Otras familias de color como Lab o CalGray son asunto del preflight completo, así que pasa el archivo terminado por un validador externo como parte de tu build para obtener el veredicto de conformidad completo. La imagen más amplia de la salida conforme está en la guía de salida PDF/A, PDF/X y PDF/UA con HotPDF, y cablear un validador en una pipeline desatendida queda cubierto en automatizar checks de preflight PDF en Delphi
HotPDF es un componente PDF VCL nativo para Delphi y C++Builder con salida PDF/X, PDF/A y PDF/UA, APIs de color CMYK y directo, y soporte de OutputIntent ICC. Detalles, licencias y descarga de prueba están en la página del producto del componente PDF HotPDF para Delphi