Una casa de prensas devuelve el trabajo: la misma tinta directa separada en dos placas. HotPDF lo previene en el momento de autoría escribiendo NChannel como la forma DeviceN de cinco elementos de ISO 32000-2 y manteniendo un espacio alternativo canónico y una transformación de tinte por nombre de tinta directa para todo el documento, rechazando la segunda definición en conflicto en lugar de emitirla
NChannel no es un nombre de familia de espacios de color
Lo primero que hay que desaprender es el nombre en sí. NChannel no es una familia de espacios de color al modo en que Separation y DeviceN son familias. ISO 32000-2 §8.6.6.5 lo describe como un subtipo de DeviceN, así que un espacio NChannel conforme se escribe como el arreglo de cinco elementos [/DeviceN names alternateSpace tintTransform attributes], y el diccionario de atributos lleva /Subtype /NChannel. No existe ningún arreglo [/NChannel ...] en la especificación. Si alguna vez construyeron uno a mano y vieron a un RIP encogerse de hombros, esa es la razón
HotPDF se equivocó con esto una vez y luego lo corrigió, algo que vale decir sin rodeos porque moldea cómo se comporta el componente hoy. Versiones anteriores de HotPDF emitían la forma de nombre de familia. THotPDF.RegisterNChannelColorSpace ahora emite solo la forma estándar de DeviceN más atributos, y como el subtipo NChannel llegó en PDF 1.6, el punto de entrada del productor se protege con RequirePDFVersion(pdf16, ...) y simplemente declina en objetivos más viejos. El lado de renderizado es deliberadamente más indulgente que el escritor: HPDFResolveColorSpace todavía acepta el token legado /NChannel como familia DeviceN para que los archivos del escritor viejo sigan renderizando, pero todo lo que HotPDF escribe de vuelta usa la codificación estándar. Indulgente en la entrada, estricto en la salida es la asimetría correcta aquí, porque su lector tiene que lidiar con archivos que no creó, mientras que su escritor no tiene esa excusa
¿Por qué un nombre de tinta directa termina en dos placas?
Porque un nombre de colorante directo es una identidad de placa a nivel del documento, no un argumento local. Dos llamadas que ambas nombran Orange pero entregan un espacio alternativo distinto, o el mismo espacio alternativo con una transformación de tinte distinta, describen dos tintas diferentes que casualmente comparten una etiqueta. Un RIP que construye separaciones no tiene forma de reconciliar eso, así que hace lo único honesto y les entrega dos placas. HotPDF mantiene por eso una firma canónica por documento para cada nombre de colorante. RegisterSpotColorantDefinition compone esa firma a partir del espacio de color alternativo y la forma de la función de tinte, y cada RegisterSeparation, RegisterSeparationFunc, RegisterSeparationLUT y definición de spot NChannel pasa por ella. Cuando una segunda definición discrepa, la llamada lanza una excepción en lugar de registrar silenciosamente una segunda variante, y el mensaje es deliberadamente específico sobre el modo de falla, porque la alternativa es descubrirlo en una prueba de placas tres semanas después
// Orange ya estaba registrado contra DeviceCMYK con la
// transformación de tinte 0 / 0.55 / 1 / 0 a tinte pleno.
Conflicting := Pdf.RegisterExponentialFunction(
Domain1, NoInk, OtherOrangeCMYK, 1, []);
try
Pdf.RegisterSeparationFunc('Orange', 'DeviceCMYK', Conflicting);
except
on E: Exception do
// 'Spot colourant "Orange" has inconsistent alternate colour
// space or tint definition in this document'
LogPrepressWarning(E.Message);
end;
Dividir los nombres maestros en proceso y spot
Un NChannel completo tiene que dar cuenta de cada uno de sus nombres de colorante maestros exactamente una vez, como componente de proceso o como colorante directo. La sobrecarga avanzada de RegisterNChannelColorSpace toma los ColorantNames maestros, los ProcessColorantNames, el espacio alternativo, la transformación de tinte global, un arreglo de records THPDFNChannelSpotColorant y un orden de impresión opcional. Cada record de spot lleva su propio nombre, su propia transformación de tinte Separation de una entrada, una solidez opcional y una función de ganancia de punto opcional. La transformación de tinte global debe mapear N entradas al conteo de componentes del espacio alternativo; cada tinte de spot debe mapear una entrada a ese mismo conteo
const
Colorants: array[0..4] of AnsiString =
('Cyan', 'Magenta', 'Yellow', 'Black', 'Orange');
ProcessNames: array[0..3] of AnsiString =
('Cyan', 'Magenta', 'Yellow', 'Black');
Order: array[0..4] of AnsiString =
('Yellow', 'Magenta', 'Cyan', 'Orange', 'Black');
Domain5: array[0..9] of Single = (0, 1, 0, 1, 0, 1, 0, 1, 0, 1);
Range4: array[0..7] of Single = (0, 1, 0, 1, 0, 1, 0, 1);
Domain1: array[0..1] of Single = (0, 1);
NoInk: array[0..3] of Single = (0, 0, 0, 0);
OrangeCMYK: array[0..3] of Single = (0, 0.55, 1, 0);
GainC0: array[0..0] of Single = (0);
GainC1: array[0..0] of Single = (1);
var
Pdf: THotPDF;
Spots: array[0..0] of THPDFNChannelSpotColorant;
CSName: AnsiString;
begin
Pdf.Version := pdf20;
Pdf.BeginDoc;
Spots[0].Name := 'Orange';
Spots[0].TintTransform := Pdf.RegisterExponentialFunction(
Domain1, NoInk, OrangeCMYK, 1, []);
Spots[0].HasSolidity := True;
Spots[0].Solidity := 0.82;
Spots[0].DotGainFunction := Pdf.RegisterExponentialFunction(
Domain1, GainC0, GainC1, 1, []);
CSName := Pdf.RegisterNChannelColorSpace(Colorants, ProcessNames,
'DeviceCMYK',
Pdf.RegisterPostScriptFunction(Domain5, Range4,
'{ pop pop pop pop pop 0 0 0 0 }'),
Spots, Order);
De esa llamada HotPDF emite el diccionario de atributos que pide la especificación: /Subtype /NChannel, un diccionario /Process cuyo /ColorSpace es el espacio de proceso y cuyo /Components lista los nombres de proceso en el orden de componentes de ese espacio, un diccionario /Colorants que contiene un arreglo real [/Separation name alternate tintfn] por cada spot, y un diccionario /MixingHints que lleva /Solidities, /PrintingOrder y /DotGain cuando ustedes los suministraron. El nombre de recurso devuelto va a SetFillColorSpace o SetStrokeColorSpace exactamente como los espacios más simples cubiertos en el artículo sobre renderizado de colores directos Separation y DeviceN
¿Qué rechaza realmente la comprobación de consistencia?
Rechaza la incoherencia estructural dentro del espacio, y lo hace antes de que se escriba un solo objeto. Los nombres de colorante deben ser únicos y no deben ser vacíos, All ni None. Las definiciones de proceso y spot juntas deben cubrir los nombres maestros exactamente, sin que ningún colorante aparezca en ambos roles y sin que quede ninguno sin definir. Cuando el espacio alternativo es DeviceCMYK, los componentes de proceso deben ser Cyan, Magenta, Yellow, Black en ese orden, y el conteo de nombres de proceso debe coincidir con el conteo de componentes del alternativo. Cada transformación de tinte y función de ganancia de punto debe ser un objeto de función indirecto con la aridad de entrada y salida correcta. La solidez debe ser un valor finito dentro de 0..1. El orden de impresión debe estar vacío o ser una permutación completa de los nombres maestros, nunca una lista parcial. Lo que no hace es juzgar el color: nada aquí verifica que su transformación de tinte Orange realmente se parezca a la tinta del tarro, que su construcción CMYK sea un proxy sensato o que la solidez que suministraron coincida con el comportamiento medido sobre el sustrato. Esas son preguntas de prensa y de medición, y el componente no tiene autoridad para responderlas. La sobrecarga más simple de solo proceso es todavía más estricta por diseño: produce un NChannel de solo proceso y se niega deliberadamente a aceptar nombres de spot, porque escribir un spot en el arreglo de nombres sin una entrada /Colorants correspondiente produciría un archivo estructuralmente no conforme, y fabricar una definición por defecto sería peor que fallar
Los output intents PDF/X-6n deben cubrir cada spot registrado
Un archivo PDF/X-6n de N colorantes declara sus colorantes dos veces, y las dos declaraciones deben concordar. AddPDFX6ExternalOutputIntent escribe la referencia del perfil ICC externo con su ColorantTable, y antes de hacerlo, ValidateRegisteredSpotOutputColorants recorre cada spot que el documento haya registrado y lanza una excepción si alguno falta en la tabla. La comprobación corre en ambas direcciones: una vez que un output intent ha publicado su lista de colorantes, un registro posterior de spot para un nombre fuera de esa lista también se rechaza. AddPDFX6ExternalOutputIntentSpotData agrega los metadatos por tinta encima de eso, y hace cumplir sus propias reglas, notablemente que un colorante lleva o un valor de solidez o datos espectrales CxF/X-4, nunca ambos. Esta es la misma superficie de conformidad discutida en la pieza sobre validación PDF/A, PDF/X y PDF/UA
Spectral := TMemoryStream.Create;
try
LoadCxFForInk('Orange', Spectral); // carga ISO 17972-4
Pdf.AddPDFX6ExternalOutputIntentSpotData(
'ECG-5', 'Five-colour output condition',
'https://profiles.example.com/ecg-5.icc', '5CLR',
Colorants,
'00112233445566778899AABBCCDDEEFF', #4#3#0#0,
['Cyan'], [0.70], // solidez para un colorante
Order,
['Orange'], [Spectral]); // datos espectrales para otro
finally
Spectral.Free;
end;
Dos detalles operativos son fáciles de pasar por alto. El registro que respalda todo esto es por documento y se limpia en los límites del documento, así que cargar un archivo nuevo en la misma instancia de THotPDF no hereda las identidades de spot del documento anterior; ese aislamiento es el punto, ya que una firma filtrada rechazaría trabajo perfectamente válido en el siguiente trabajo. Y el lado del perfil tiene límites duros propios: un output intent externo requiere un objetivo PDF 2.0, una URL de perfil absoluta HTTP o HTTPS, una firma de espacio de color ICC de cuatro bytes, y para PDF/X-6n entre 2 y 15 colorantes con una firma correspondiente de 2CLR a FCLR
Dónde se detiene la comprobación
El manejo de CxF/X-4 es la parte de la que hay que ser honestos. HotPDF aplica comprobaciones de seguridad estructurales acotadas al stream espectral y confirma que la identidad de tinta dentro de él coincide con el colorante que nombraron. Limita el tamaño de la carga, rechaza bytes null incrustados, se niega a cualquier stream que contenga una declaración DOCTYPE o ENTITY, exige una raíz CxF reconocible y exige exactamente un elemento SpotInkCharacterisation que lleve exactamente un SpotInkName igual al nombre de su colorante. Eso es una compuerta contra entrada malformada y hostil, no un validador de esquemas. No es una implementación completa de ISO 17972-4, no verifica sus mediciones espectrales y no audita a la inversa grafos de objetos arbitrarios de terceros preexistentes ni las tablas de colorantes internas dentro de un perfil ICC incrustado. Si su flujo de trabajo depende de conformidad CxF completa, validen el archivo con una herramienta dedicada antes de entregárselo al componente
Una restricción adyacente muerde a quienes nunca esperaron encontrarla. Una máscara suave de luminosidad no puede usar Separation, DeviceN ni NChannel como /CS de su grupo de transparencia; el espacio de mezcla del grupo tiene que ser un espacio de dispositivo o basado en CIE, así que la pintura de spot dentro del grupo se resuelve a través de su transformación de tinte hacia el espacio alternativo antes de calcular la luminosidad. RegisterLuminositySoftMaskState construye un grupo DeviceGray precisamente para que esto no pueda salir mal por accidente. La consecuencia práctica es que un diseño cargado de spots enmascarado de esta forma se evalúa a través de su proxy CMYK, no de su tinta, lo que importa al compararlo contra una prueba separada como se describe en las notas sobre pruebas de overprint y dispositivos de render
Nada de esto elimina la necesidad de una prueba de placas, pero mueve toda una clase de rechazos de prensas de la sala de prensa de vuelta al build. Las APIs de NChannel, Separation y output intent PDF/X-6n descritas aquí se entregan en el HotPDF Delphi Component estándar para Delphi y C++Builder, donde la referencia documenta la disposición completa de los records y las condiciones exactas bajo las que cada llamada falla cerrada