Las funciones PDF son uno de los rincones más silenciosos de la especificación. La mayoría de los desarrolladores las conocen una vez, como el elemento que un sombreado axial Tipo 2 necesita para desvanecerse entre dos colores, y nunca vuelven a mirar. Eso es una lástima, porque la maquinaria de funciones es un pequeño evaluador de propósito general que el formato reutiliza para sombreados, funciones de transferencia, funciones de punto de medio tono, tintas de separación y curvas de transferencia de máscara suave. De los cuatro tipos de funciones, el Tipo 0 es el más potente y el menos comprendido. Es una función muestreada: una cuadrícula multidimensional de valores de salida entre los cuales el lector realiza la interpolación. Debido a que la cuadrícula puede contener cualquier número que coloque en ella, una función Tipo 0 puede expresar un mapeo no lineal arbitrario, que es la forma exacta de una tabla de búsqueda de colores.
Este artículo recorre el diccionario Tipo 0 tal como lo define la norma ISO 32000-1 en el §7.10.2, y luego muestra los dos casos que más importan en un flujo de trabajo de documentos: una tabla LUT de corrección de color de tres entradas de RGB a RGB, y una transformación de tinta de color plano de una entrada. El mismo constructor de funciones muestreadas sirve para ambos, y la diferencia entre ellos es enteramente una cuestión de cuántas entradas tiene la cuadrícula.
Una función muestreada es una cuadrícula que el lector interpola
Una función Tipo 0 asigna un vector de m entradas a un vector de n salidas almacenando muestras en una cuadrícula regular e interpolando entre ellas. La norma ISO 32000-1 §7.10.2 enumera las claves que describen esa cuadrícula. /Domain contiene dos números por entrada, el límite inferior y superior de cada eje de entrada. /Range contiene dos números por componente de salida. /Size es una matriz de m enteros que indica el número de muestras a lo largo de cada eje de entrada, por lo que una cuadrícula que tiene doce muestras por lado en tres dimensiones tiene /Size [12 12 12] y almacena 1.728 puntos de cuadrícula. /BitsPerSample establece la precisión de cada valor almacenado; HotPDF acepta 1, 2, 4, 8, 12, 16, 24, y 32 bits, coincidiendo con los valores que permite la Tabla 38.
El flujo de muestras se lee en un orden fijo. La primera dimensión de entrada varía más rápido, luego la segunda, y así sucesivamente, y en cada punto de la cuadrícula los n componentes de salida se almacenan en orden. Para una tabla de RGB a RGB, eso representa tres bytes por punto de cuadrícula a ocho bits, dispuestos como salida-rojo, salida-verde, salida-azul, recorriendo la entrada roja primero. Dos claves más mapean el mundo continuo en la cuadrícula de enteros. /Encode mapea cada entrada desde su intervalo /Domain al rango de índice de muestra 0 a Size[i] - 1, y /Decode mapea los enteros almacenados sin procesar de vuelta a los intervalos /Range. Cuando se dejan en sus valores predeterminados, una entrada que abarca [0 1] aterriza limpiamente en la cuadrícula completa y un byte almacenado de 255 se decodifica en el extremo superior de su rango de salida, que es lo que desea una tabla LUT de color normalizada en [0,1].
Orden 1 frente a Orden 3
Entre los puntos de la cuadrícula, el lector debe interpolar, y /Order elige cómo. /Order 1 es interpolación multilineal: lineal a lo largo de un eje, bilineal en dos, trilineal en tres. Es rápida, es exactamente lo que hace el hardware en la mayoría de los visores y, para una transformación de color suave, suele ser indistinguible de algo más complejo. /Order 3 solicita interpolación de spline cúbico, que ajusta una curva más suave a través de las muestras a costa de más trabajo y una región de soporte más amplia alrededor de cada punto evaluado.
El equilibrio es la densidad de la cuadrícula frente a la suavidad de la curva. El orden cúbico vale la pena cuando la cuadrícula es gruesa y el mapeo tiene una curvatura visible, porque una línea recta entre dos muestras distantes puede aplanar una curva de tono de una manera que el ojo detecta en los degradados. Una vez que la cuadrícula es densa, los segmentos son lo suficientemente cortos como para que la interpolación lineal siga la curva de cerca y el orden cúbico aporte poco. Una regla práctica es recurrir a /Order 3 solo con cuadrículas pequeñas o transformaciones pronunciadas y, de lo contrario, dejarlo en el valor predeterminado lineal. Tenga en cuenta que /Order se aplica únicamente a las funciones Tipo 0, y HotPDF rechaza cualquier valor que no sea 1 o 3.
La LUT 3D: tres entradas, tres salidas
Una corrección de color de RGB a RGB es el caso de libro de texto para una cuadrícula de tres entradas, la clásica LUT 3D utilizada en la gradación de color y la coincidencia de dispositivos. Cada eje del cubo es un canal de entrada, cada punto de la cuadrícula almacena el triple RGB corregido para esa coordenada de entrada, y el lector interpola trilinealmente las muestras de las esquinas alrededor de cualquier color entrante. Tres entradas son inevitables aquí porque el rojo corregido puede depender del verde y el azul de entrada, no solo del rojo de entrada; una curva por canal no puede expresar la diafonía entre canales, pero un cubo sí puede.
HotPDF crea el flujo Tipo 0 a través de RegisterSampledFunction, que toma el /Domain, /Range, /Size, /BitsPerSample y los bytes de muestra directamente y devuelve el objeto de función. Para un cubo normalizado estándar, pasa límites [0,1] en los tres ejes de entrada y en las tres salidas, un tamaño N x N x N y la tabla de muestras aplanada. El constructor valida que el recuento de bytes coincida con la cuadrícula: para una profundidad alineada por bytes, espera OutputCount x (BitsPerSample div 8) x el producto de los tamaños, y lanza una excepción si la matriz tiene una longitud incorrecta, de modo que un paso mal calculado falla ruidosamente en el registro en lugar de representarse como basura más tarde.
const
N = 17; // 17 x 17 x 17 cube, the common ICC LUT resolution
var
LutFn: THPDFStreamObject;
Samples: TBytes;
begin
// Fill Samples with N*N*N grid points, 3 bytes each (R,G,B output),
// red input varying fastest. Build the corrected triple for each
// grid coordinate with your ICC-managed conversion, then store it.
SetLength(Samples, N * N * N * 3);
BuildCorrectedCube(Samples, N); // your color-managed fill
LutFn := Pdf.RegisterSampledFunction(
[0,1, 0,1, 0,1], // /Domain: three input axes on [0,1]
[0,1, 0,1, 0,1], // /Range: three output channels on [0,1]
[N, N, N], // /Size: the cube resolution per axis
8, // /BitsPerSample
Samples,
1); // /Order 1 = trilinear
end;
La corrección colorimétrica del cubo reside en cómo lo llene, no en la función PDF. El camino honesto es calcular cada punto de la cuadrícula a través de una conversión gestionada por ICC, el mismo motor que gestiona una prueba en pantalla, de modo que los números de la cuadrícula tengan significado frente a un perfil de origen y destino definido. Registre los perfiles que limitan la conversión con RegisterICCProfile, que registra un espacio de color ICCBased (1, 3 o 4 componentes) y devuelve un nombre de recurso que puede adjuntar al contenido que alimenta la LUT. La función Tipo 0 lleva la tabla de interpolación; el perfil ICC lleva el significado de los extremos.
El caso 1D: una transformación de tinta de color plano
Los espacios de color de separación se apoyan en la misma maquinaria para un trabajo completamente diferente. Un espacio de separación, definido en la norma ISO 32000-1 §8.6.6.4, representa un único colorante, una tinta plana como una Pantone o un barniz, emparejando un nombre con una transformación de tinta: una función que mapea el valor de tinta unidimensional, 0 para nada de tinta a 1 para tinta completa, en un espacio de color alternativo que el dispositivo realmente pueda representar, normalmente CMYK. Esa transformación de tinta es frecuentemente una función Tipo 0, y ahora la cuadrícula tiene exactamente un eje de entrada.
Este es el contraste claro con la LUT 3D. Una tinta plana es un grado de libertad, por lo que su transformación de tinta necesita una entrada y la cuadrícula es una línea de muestras, cada una de las cuales contiene el valor CMYK (u otra alternativa) a ese nivel de tinta. El cubo RGB necesita tres entradas porque su dominio es tridimensional y los canales interactúan. Mismo tipo de función, mismas reglas de interpolación, diferente dimensionalidad; la especificación reutiliza un evaluador y permite que /Size decida si está recorriendo una línea, un plano o un cubo. HotPDF envuelve toda la separación en RegisterSeparationLUT, que compila internamente la transformación de tinta Tipo 0 de una entrada a partir de una matriz de bytes plana y devuelve el nombre del recurso del espacio de color.
var
SpotCS: AnsiString;
begin
// Four CMYK output bytes per tint grid point, tint domain [0..1].
// Here 0% ink -> all zero, 100% ink -> a rich spot build,
// with two interior steps; the tint transform interpolates between.
SpotCS := Pdf.RegisterSeparationLUT(
'PANTONE 286 C', // colorant name
'DeviceCMYK', // alternate color space
[ 0, 0, 0, 0, // tint 0.00 -> 0,0,0,0
90, 60, 0, 0, // tint 0.33
100, 80, 0, 10, // tint 0.66
100, 72, 0, 18]); // tint 1.00 -> full ink build
// Use SpotCS with SetFillColorSpace / SetFillColor on a page.
end;
El recuento de muestras debe ser un número entero de puntos de cuadrícula: un múltiplo positivo del recuento de componentes del espacio alternativo, y al menos dos puntos para que haya un segmento que interpolar. Si pasa tres bytes por punto contra una alternativa CMYK, la llamada lo rechaza, la misma validación defensiva que aplica el constructor 3D, que es lo que desea de una función que de otro modo fallará silenciosamente en el momento de la impresión.
Dónde vuelve a aparecer la misma maquinaria
Una vez que ve el Tipo 0 como una tabla de interpolación genérica, otras dos características de control de dispositivos dejan de parecer casos especiales. Una función de transferencia ajusta los valores de los componentes en su camino hacia el dispositivo de salida, y es solo una función por canal; HotPDF la registra como un ExtGState a través de RegisterTransferFunctionState, que acepta una función combinada o una matriz de funciones por canal. Debido a que esas funciones son objetos de función comunes, puede pasarle el mismo THPDFStreamObject que devuelve RegisterSampledFunction y controlar una curva de transferencia a partir de una tabla muestreada en lugar de una fórmula.
var
ToneFn: THPDFStreamObject;
GsName: AnsiString;
begin
// A single-input, single-output sampled tone curve on [0,1].
ToneFn := Pdf.RegisterSampledFunction(
[0,1], [0,1], [256], 8, ToneCurveBytes, 1);
// Apply it to all channels as a combined /TR2 transfer function.
GsName := Pdf.RegisterTransferFunctionState(ToneFn, []);
// Select GsName on the page before drawing the affected content.
end;
La generación de negro y la eliminación de subcolor se encuentran en la misma familia. Cuando un dispositivo convierte RGB a CMYK, decide cuánta cantidad de componente gris llevar como tinta negra, y la especificación expresa esa decisión como una función, las entradas /BG2 y /UCR2 de un diccionario de estado gráfico, cada una de las cuales es una curva de entrada única desde el gris calculado hasta una cantidad de negro. Esas también son funciones Tipo 0 cuando desea una curva medida en lugar de una analítica, creadas de la misma manera a través de RegisterSampledFunction y colocadas en el estado gráfico. La lección que vale la pena conservar es que la función PDF nunca es el lugar donde ocurre la gestión del color; es la tabla de búsqueda que lleva una decisión que usted tomó con un motor de color real, y Tipo 0 es el único tipo de función lo suficientemente flexible como para llevar cualquier decisión.
Para obtener una visión más amplia de cómo se emiten las fuentes, las imágenes y los recursos de color en un documento terminado, consulte nuestro tutorial sobre la salida de informes con fuentes e imágenes. Cuando la salida debe sobrevivir a una comprobación preliminar de archivo o impresión, las reglas de espacio de color e intención de salida cubiertas en la guía de validación de PDF/A, PDF/X y PDF/UA gobiernan cuáles de estas funciones están permitidas y cómo debe etiquetarse el color del dispositivo. Todo esto se incluye en el Componente HotPDF para Delphi y C++Builder, junto con las API de sombreado, ICC y separación que se basan en el mismo núcleo Tipo 0.