Las funciones PDF son uno de los rincones más silenciosos de la especificación. La mayoría de los desarrolladores se topan con ellas una vez, como lo que necesita un sombreado axial Type 2 para desvanecerse entre dos colores, y nunca vuelven a mirarlas. 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 semitono, tintes de separación y curvas de transferencia de máscara suave. De los cuatro tipos de funciones, Type 0 es la más poderosa y la menos comprendida. Es una función muestreada (sampled function): una cuadrícula multidimensional de valores de salida entre los cuales el lector interpola. Debido a que la cuadrícula puede contener cualquier número que se le asigne, una función Type 0 puede expresar un mapeo no lineal arbitrario, que es la forma exacta de una tabla de búsqueda de colores (color lookup table)
Este artículo recorre el diccionario Type 0 tal como lo define la norma ISO 32000-1 en la sección 7.10.2, y luego muestra los dos casos que más importan en un flujo de procesamiento de documentos: una LUT de corrección de color RGB a RGB de tres entradas y una transformación de tinte de color plano de una entrada. El mismo constructor de funciones muestreadas sirve para ambos, y la diferencia entre ellos es completamente 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 Type 0 mapea 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 describe 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 un arreglo 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 RGB a RGB que es de tres bytes por punto de cuadrícula a ocho bits, el diseño es salida de rojo, salida de verde, salida de azul, barriendo primero a través de la entrada de rojo. 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 de 0 a Size[i] - 1, y /Decode mapea los enteros almacenados sin procesar nuevamente a los intervalos de /Range. Cuando se dejan en sus valores predeterminados, una entrada que abarca [0 1] aterriza limpiamente en toda la cuadrícula y un byte almacenado de 255 se decodifica en la parte superior de su rango de salida, que es lo que requiere una LUT de color normalizada a [0,1]
Orden 1 frente a Orden 3
Entre los puntos de la cuadrícula, el lector tiene que interpolar, y /Order elige cómo. /Order 1 es interpolación multilineal: lineal a lo largo de un eje, bilineal a través de dos, trilineal a través de tres. Es rápido, es exactamente lo que hace el hardware en la mayoría de los visores, y para una transformación de color suave generalmente es indistinguible de algo más complejo. /Order 3 solicita una interpolación de spline cúbico (cubic-spline interpolation), 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 está en la densidad de la cuadrícula frente a la suavidad de la curva. El orden cúbico justifica su uso 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 capta 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 optar por /Order 3 solo con cuadrículas pequeñas o transformaciones pronunciadas, y de lo contrario dejarlo en el valor lineal predeterminado. Tenga en cuenta que /Order se aplica solo a las funciones Type 0, y HotPDF rechaza cualquier valor distinto de 1 o 3
La LUT 3D: tres entradas, tres salidas
Una corrección de color de RGB a RGB es el caso de manual para una cuadrícula de tres entradas, la clásica LUT 3D utilizada en la corrección de color (color grading) y la calibración 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 de manera trilineal las muestras de las esquinas alrededor de cualquier color entrante. Tres entradas son inevitables aquí porque el rojo corregido puede depender del verde y azul de entrada, no solo del rojo de entrada; una curva por canal no puede expresar la interferencia de canales (channel crosstalk), pero un cubo sí puede
HotPDF construye el flujo Type 0 a través de RegisterSampledFunction, que toma /Domain, /Range, /Size, /BitsPerSample y los bytes de muestra directamente y devuelve el objeto de la función. Para un cubo normalizado estándar, se pasan los límites [0,1] en los tres ejes de entrada y las tres salidas, un tamaño de 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 a bytes, espera OutputCount x (BitsPerSample div 8) x el producto de los tamaños, y genera una excepción si el arreglo tiene la longitud incorrecta, por lo que un stride mal calculado falla ruidosamente en el registro en lugar de renderizarse como basura más tarde
const
N = 17; // Cubo de 17 x 17 x 17, la resolución común de LUT ICC
var
LutFn: THPDFStreamObject;
Samples: TBytes;
begin
// Llenar Samples con puntos de cuadrícula N*N*N, 3 bytes cada uno (salida R,G,B),
// la entrada roja varía más rápido. Construir el triple corregido para cada
// coordenada de cuadrícula con su conversión administrada por ICC, luego almacenarlo.
SetLength(Samples, N * N * N * 3);
BuildCorrectedCube(Samples, N); // su relleno administrado por color
LutFn := Pdf.RegisterSampledFunction(
[0,1, 0,1, 0,1], // /Domain: tres ejes de entrada en [0,1]
[0,1, 0,1, 0,1], // /Range: tres canales de salida en [0,1]
[N, N, N], // /Size: la resolución del cubo por eje
8, // /BitsPerSample
Samples,
1); // /Order 1 = trilineal
end;
La corrección colorimétrica del cubo reside en cómo se llena, no en la función PDF. El camino honesto es calcular cada punto de la cuadrícula a través de una conversión administrada por ICC, el mismo motor que impulsa una prueba de color en pantalla (soft-proof), para que los números en la cuadrícula signifiquen algo en relación con 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 el nombre de un recurso que puede adjuntar al contenido del que se alimenta la LUT. La función Type 0 transporta la tabla de interpolación; el perfil ICC transporta el significado de los extremos
El caso 1D: una transformación de tinte de color plano
Los espacios de color de Separación (Separation) se apoyan en la misma maquinaria para un trabajo completamente diferente. Un espacio de Separation, definido en ISO 32000-1 §8.6.6.4, representa un solo colorante, una tinta plana como Pantone o un barniz, emparejando un nombre con una transformación de tinte: una función que mapea el valor de tinte unidimensional, 0 para sin tinta a 1 para tinta completa, a un espacio de color alternativo que el dispositivo puede renderizar realmente, generalmente CMYK. Esa transformación de tinte suele ser una función Type 0, y ahora la cuadrícula tiene exactamente un eje de entrada
Este es un claro contraste con la LUT 3D. Una tinta plana representa un grado de libertad, por lo que su transformación de tinte necesita una entrada y la cuadrícula es una línea de muestras, cada una con el valor CMYK (u otro alternativo) en ese nivel de tinte. 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 se está recorriendo una línea, un plano o un cubo. HotPDF envuelve toda la separación en RegisterSeparationLUT, que construye internamente la transformación de tinte Type 0 de una entrada a partir de un arreglo plano de bytes y devuelve el nombre del recurso del espacio de color
var
SpotCS: AnsiString;
begin
// Cuatro bytes de salida CMYK por punto de cuadrícula de tinte, dominio de tinte [0..1].
// Aquí 0% de tinta -> todo cero, 100% de tinta -> una mezcla rica de colores planos,
// con dos pasos intermedios; la transformación de tinte interpola entre ellos.
SpotCS := Pdf.RegisterSeparationLUT(
'PANTONE 286 C', // nombre del colorante
'DeviceCMYK', // espacio de color alternativo
[ 0, 0, 0, 0, // tinte 0.00 -> 0,0,0,0
90, 60, 0, 0, // tinte 0.33
100, 80, 0, 10, // tinte 0.66
100, 72, 0, 18]); // tinte 1.00 -> mezcla de tinta completa
// Usar SpotCS con SetFillColorSpace / SetFillColor en una página.
end;
El conteo de muestras tiene que ser un número entero de puntos en la cuadrícula: un múltiplo positivo del conteo de componentes del espacio alternativo, y al menos dos puntos para que haya un segmento que interpolar. Pase tres bytes por punto contra una alternativa CMYK y la llamada lo rechazará, la misma validación defensiva que aplica el constructor 3D, que es lo que se desea de una función que de otro modo fallaría silenciosamente al momento de imprimir
Dónde vuelve a aparecer la misma maquinaria
Una vez que ve el Type 0 como una tabla de interpolación genérica, dos características más 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 un arreglo de funciones por canal. Debido a que esas funciones son objetos de funciones ordinarios, puede pasarle el mismo THPDFStreamObject que RegisterSampledFunction devuelve y controlar una curva de transferencia desde una tabla muestreada en lugar de una fórmula
var
ToneFn: THPDFStreamObject;
GsName: AnsiString;
begin
// Una curva de tono muestreada de una sola entrada y salida en [0,1].
ToneFn := Pdf.RegisterSampledFunction(
[0,1], [0,1], [256], 8, ToneCurveBytes, 1);
// Aplicarla a todos los canales como una función de transferencia /TR2 combinada.
GsName := Pdf.RegisterTransferFunctionState(ToneFn, []);
// Seleccionar GsName en la página antes de dibujar el contenido afectado.
end;
La generación de negro y la eliminación de subcolor pertenecen a la misma familia. Cuando un dispositivo convierte RGB a CMYK, decide qué cantidad del 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 con una curva de entrada única desde el gris calculado hasta una cantidad de negro. Esas también son funciones Type 0 cuando se desea una curva medida en lugar de una analítica, construidas de la misma manera a través de RegisterSampledFunction y colocadas en el estado gráfico. La lección que vale la pena guardar es que la función PDF nunca es donde ocurre la gestión del color; es la tabla de búsqueda que transporta una decisión que tomó con un motor de color real, y Type 0 es el único tipo de función lo suficientemente flexible como para transportar cualquier decisión en absoluto
Para obtener una imagen más amplia de cómo se emiten las fuentes, las imágenes y los recursos de color en un documento final, consulte nuestro tutorial sobre la salida de informes con fuentes e imágenes. Cuando el archivo de salida debe sobrevivir a una comprobación previa de archivo o impresión, las reglas de espacio de color e intención de salida que se tratan en la guía de validación de PDF/A, PDF/X y PDF/UA rigen cuáles de estas funciones están permitidas y cómo se debe etiquetar 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 Type 0