As funções PDF são uma das áreas menos exploradas da especificação. A maioria dos programadores depara-se com elas apenas uma vez, como o elemento de que um sombreado axial do Tipo 2 necessita para realizar o desvanecimento entre duas cores, e não volta a olhar para elas. É uma pena, porque o mecanismo de funções é um pequeno avaliador de âmbito geral que o formato reutiliza para sombreados, funções de transferência, funções de ponto de meio-tom (halftone), tonalidades de separação e curvas de transferência de máscara de transparência (soft-mask). Dos quatro tipo de funções, o Tipo 0 é o mais poderoso e o menos compreendido. Trata-se de uma função amostrada (sampled function): uma grelha multidimensional de valores de saída entre os quais o leitor interpola. Dado que a grelha pode conter quaisquer números que nela coloque, uma função do Tipo 0 pode expressar um mapeamento não linear arbitrário, que é a definição exata de uma tabela de correspondência de cores (color lookup table - LUT).
Este artigo aborda o dicionário do Tipo 0 tal como a norma ISO 32000-1 o define na secção 7.10.2, e depois apresenta os dois casos mais relevantes num fluxo de processamento de documentos: uma LUT de correção de cor de três entradas (RGB para RGB) e uma transformação de tonalidade de cor spot de uma entrada. O mesmo construtor de funções amostradas serve ambos os casos, e a diferença entre eles reside inteiramente no número de entradas que a grelha possui.
Uma função amostrada é uma grelha que o leitor interpola
Uma função do Tipo 0 mapeia um vetor de m entradas para um vetor de n saídas, guardando amostras numa grelha regular e interpolando entre elas. A norma ISO 32000-1, secção 7.10.2, lista as chaves que descrevem essa grelha. /Domain contém dois números por entrada, que representam o limite inferior e superior de cada eixo de entrada. /Range guarda dois números por componente de saída. /Size é uma matriz de m inteiros que indica o número de amostras ao longo de cada eixo de entrada; assim, uma grelha com doze amostras de lado em três dimensões tem /Size [12 12 12] e guarda 1.728 pontos de grelha. /BitsPerSample define a precisão de cada valor guardado; o HotPDF aceita 1, 2, 4, 8, 12, 16, 24 e 32 bits, correspondendo aos valores permitidos pela Tabela 38.
O fluxo de amostras é lido numa ordem fixa. A primeira dimensão de entrada varia mais rapidamente, seguida pela segunda e assim sucessivamente, e em cada ponto da grelha os n componentes de saída são guardados por ordem. Para uma tabela RGB para RGB, isto representa três bytes por ponto de grelha a oito bits, organizados como saída vermelha, saída verde, saída azul, percorrendo primeiro a entrada vermelha. Duas chaves adicionais mapeiam o domínio contínuo na grelha de inteiros. /Encode mapeia cada entrada do seu intervalo /Domain para o intervalo de índices de amostras de 0 a Size[i] - 1, e /Decode mapeia os inteiros guardados de volta para os intervalos /Range. Quando mantidos nos seus valores predefinidos, uma entrada no intervalo [0 1] coincide precisamente com a grelha completa e um byte guardado de 255 descodifica-se para o valor máximo do seu intervalo de saída, que é exatamente o pretendido para uma LUT de cores normalizada em [0,1].
Ordem 1 versus Ordem 3
O leitor tem de efetuar a interpolação entre os pontos da grelha, e a chave /Order define como o fazer. /Order 1 representa a interpolação multilinear: linear ao longo de um eixo, bilinear em dois e trilinear em três. É rápida, corresponde exatamente ao que o hardware da maioria dos visualizadores faz e, para uma transformação de cor suave, é geralmente indistinguível de métodos mais sofisticados. /Order 3 solicita a interpolação por spline cúbica, que ajusta uma curva mais suave através das amostras à custa de maior processamento e de uma região de suporte mais ampla em torno de cada ponto avaliado.
O equilíbrio situa-se entre a densidade da grelha e a suavidade da curva. A ordem cúbica justifica-se quando a grelha é espaçada e o mapeamento apresenta uma curvatura visível, porque uma linha reta entre duas amostras distantes pode achatar uma curva de tons de uma forma que o olhar deteta nos gradientes. Assim que a grelha é densa, os segmentos são suficientemente curtos para que a interpolação linear acompanhe a curva de perto, acrescentando a cúbica muito pouco valor. Uma regra prática consiste em recorrer a /Order 3 apenas com grelhas pequenas ou transformações acentuadas; caso contrário, deve manter-se a predefinição linear. Note que a chave /Order se aplica apenas a funções do Tipo 0, e o HotPDF rejeita qualquer valor diferente de 1 ou 3.
A LUT 3D: três entradas, três saídas
Uma correção de cor RGB para RGB é o caso de estudo típico para uma grelha de três entradas, a clássica LUT 3D utilizada na gradação de cores (color grading) e na correspondência de dispositivos. Cada eixo do cubo é um canal de entrada, cada ponto da grelha guarda o triplo RGB corrigido para essa coordenada de entrada, e o leitor interpola de forma trilinear as amostras dos cantos em torno de qualquer cor de entrada. Três entradas são incontornáveis neste cenário porque o vermelho corrigido pode depender do verde e do azul de entrada, e não apenas do vermelho de entrada; uma curva por canal não consegue expressar a interferência entre canais (crosstalk), mas um cubo consegue-o.
HotPDF cria o fluxo do Tipo 0 através do método RegisterSampledFunction, que recebe diretamente os valores de /Domain, /Range, /Size, /BitsPerSample e os bytes das amostras, devolvendo o objeto da função. Para um cubo normalizado padrão, passa limites [0,1] em todos os três eixos de entrada e nas três saídas, um tamanho de N x N x N e a tabela de amostras linearizada. O construtor valida se a contagem de bytes corresponde à grelha: para uma profundidade alinhada por bytes, espera OutputCount x (BitsPerSample div 8) x o produto dos tamanhos, gerando uma exceção se a matriz tiver o comprimento incorreto, para que um desvio calculado incorretamente falhe de forma evidente no momento do registo em vez de se renderizar como conteúdo corrompido mais 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],
[0,1, 0,1, 0,1],
[N, N, N],
8,
Samples,
1);
end;
A exatidão colorimétrica do cubo depende da forma como o preenche, e não da função PDF. O caminho correto consiste em calcular cada ponto da grelha através de uma conversão gerida por ICC, o mesmo motor que controla uma prova digital (soft-proof), para que os números na grelha tenham significado face a um perfil de origem e de destino definidos. Registe os perfis que limitam a conversão com RegisterICCProfile, que grava um espaço de cores ICCBased (1, 3 ou 4 componentes) e devolve um nome de recurso que pode associar ao conteúdo alimentado pela LUT. A função do Tipo 0 transporta a tabela de interpolação; o perfil ICC transporta o significado dos pontos finais.
O caso 1D: uma transformação de tonalidade de cor spot
Os espaços de cores de separação apoiam-se no mesmo mecanismo para uma tarefa completamente distinta. Um espaço de separação (Separation space), definido na norma ISO 32000-1, secção 8.6.6.4, representa um único corante, uma tinta spot como uma Pantone ou um verniz, associando um nome a uma transformação de tonalidade (tint transform): uma função que mapeia o valor de tonalidade unidimensional, de 0 para nenhuma tinta a 1 para tinta total, para um espaço de cores alternativo que o dispositivo possa efetivamente renderizar, habitualmente CMYK. Essa transformação de tonalidade é frequentemente uma função do Tipo 0, e neste caso a grelha possui exatamente um eixo de entrada.
Este é o contraste nítido com a LUT 3D. Uma tinta spot representa um único grau de liberdade, pelo que a sua transformação de tonalidade necessita de uma entrada e a grelha é uma linha de amostras, guardando cada uma o valor CMYK (ou outro alternativo) nesse nível de tonalidade. O cubo RGB necessita de três entradas porque o seu domínio é tridimensional e os canais interagem. O mesmo tipo de função, as mesmas regras de interpolação, mas dimensionalidade diferente; a especificação reutiliza um único avaliador e deixa o /Size decidir se está a percorrer uma linha, um plano ou um cubo. HotPDF envolve toda a separação em RegisterSeparationLUT, que cria internamente a transformação de tonalidade do Tipo 0 de uma entrada a partir de uma matriz de bytes simples e devolve o nome do recurso do espaço de cores.
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;
A contagem de amostras deve ser um número inteiro de pontos de grelha: um múltiplo positivo do número de componentes do espaço alternativo e, pelo menos, dois pontos para que exista um segmento a interpolar. Se passar três bytes por ponto contra um espaço alternativo CMYK, a chamada rejeita-o, aplicando-se a mesma validação defensiva do construtor 3D, que é exatamente o pretendido para evitar que uma função falhe silenciosamente no momento da impressão.
Onde o mesmo mecanismo surge novamente
Assim que perspetiva o Tipo 0 como uma tabela de interpolação genérica, mais duas funcionalidades de controlo de dispositivo deixam de parecer casos especiais. Uma função de transferência ajusta os valores dos componentes no seu percurso para o dispositivo de saída, sendo apenas uma função por canal; o HotPDF regista-a como um ExtGState através de RegisterTransferFunctionState, que aceita uma função combinada única ou uma matriz de funções por canal. Como essas funções são objetos de função comuns, pode passar-lhes o próprio THPDFStreamObject devolvido por RegisterSampledFunction e controlar uma curva de transferência a partir de uma tabela de amostras em vez de uma conclusão analítica.
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;
A geração de preto (black generation) e a remoção de sob-cor (undercolor removal - UCR) pertencem à mesma família. Quando um dispositivo converte RGB em CMYK, decide quanta quantidade de componente cinzento deve traduzir-se em tinta preta, e a especificação expressa essa decisão como uma função, nomeadamente as entradas /BG2 and /UCR2 de um dicionário de estado gráfico, consistindo cada uma numa curva de uma única entrada que vai do cinzento calculado à quantidade de preto. Estas são também funções do Tipo 0 quando deseja uma curva medida em vez de uma analítica, criadas da mesma forma através de RegisterSampledFunction e colocadas no estado gráfico. A lição a reter é que a função PDF nunca é o local onde a gestão de cores ocorre; trata-se da tabela de correspondência que transporta uma decisão tomada por si com um motor de cor real, e o Tipo 0 é o único tipo de função suficientemente flexível para transportar qualquer decisão.
Para uma visão mais abrangente sobre a forma como tipos de letra, imagens e recursos de cor são emitidos num documento final, consulte o nosso guia de saída de relatórios com tipos de letra e imagens. Quando o resultado tem de passar num teste de arquivo ou de pré-voo (preflight) de impressão, as regras de espaço de cores e de intenção de saída (output intent) abordadas no guia de validação PDF/A, PDF/X e PDF/UA determinam quais destas funções são permitidas e como a cor do dispositivo deve ser etiquetada. Tudo isto é fornecido no HotPDF Component para Delphi e C++Builder, a par das APIs de sombreado, ICC e separação que se baseiam no mesmo núcleo do Tipo 0.