Um PDF cujo cabeçalho contém um fundo em gradiente ao estilo do Word ou Excel, quando renderizado para uma imagem, costumava retornar com um retângulo branco em branco onde a mistura de cores deveria estar. O renderizador de páginas do HotPDF honra agora o operador PDF sh (shading/sombreado) para gradientes axiais do Tipo 2 e radiais do Tipo 3: avalia a função do sombreado ao longo do eixo do gradiente e pinta a mistura, enquanto as versões anteriores descartavam silenciosamente o operador e deixavam a região branca como o papel. Os dois tipos de sombreado que cobrem quase todos os gradientes de documentos de escritório estão incluídos; os tipos de malha (mesh) não, e este artigo é honesto sobre onde se situa essa linha
Por que um gradiente PDF é renderizado como uma caixa branca?
Um gradiente PDF é uma estrutura vetorial, e não um bitmap que o renderizador possa transferir (blit). Um dicionário de sombreado (shading) (ISO 32000-1 §8.7.4.5) descreve a geometria — um eixo ou um par de círculos na matriz Coords — mais uma função de cor, e o operador sh pinta esse sombreado ao longo da região de recorte (clip region) atual, o mesmo modelo gráfico que o HotPDF expõe quando desenha caminhos e preenchimentos na canvas da página. O renderizador de páginas do HotPDF percorre o fluxo de conteúdo operador por operador, e qualquer operador para o qual não tenha um caminho de código é ignorado, de modo que o resto da página continua a ser renderizado. Antes da versão 2.335.0, o sh pertencia a esse conjunto ignorado: um gradiente de cabeçalho ou de página inteira não contribuía em nada para o resultado, pelo que o fundo aparecia a branco. O ficheiro nunca esteve corrompido. O renderizador simplesmente não tinha um manipulador (handler) para esse operador específico, e uma caixa branca é exatamente o aspeto de um manipulador em falta
A cadeia de avaliação de funções por trás de um sombreado
Os sombreados axiais do Tipo 2 e radiais do Tipo 3 contêm uma /Function que mapeia uma posição paramétrica ao longo do gradiente numa cor (ISO 32000-1 §7.10), e o avaliador de funções do HotPDF resolve o FunctionType e processa-o. A interpolação exponencial do Tipo 2 (§7.10.3) calcula C0 + t^N * (C1 - C0) com limitação de Domain e Range; as funções amostradas do Tipo 0 (§7.10.2) leem uma grelha de amostras de 8, 16 ou 32 bits e escolhem a mais próxima através do mapa Encode, o caso abordado em detalhe no nosso guia de pesquisas de cores amostradas do Tipo 0; as funções de costura (stitching) do Tipo 3 (§7.10.4) selecionam uma subfunção pela matriz Bounds e recorrem a ela. As funções de calculadora PostScript do Tipo 4 (§7.10.5) não são avaliadas — degradam-se de forma suave para uma avaliação falhada em vez de apresentarem uma cor errada, correspondendo à política conservadora que o HotPDF adota em todo o caminho de renderização. Este avaliador foi lançado na versão 2.334.0, uma versão antes do próprio operador, porque o pintor de sombreado é tão bom quanto a função subjacente
Como o HotPDF transforma um gradiente contínuo em píxeis?
O HotPDF aproxima a transição contínua com uma sequência finita de bandas de cor sólida. O pintor de sombreado percorre o eixo do gradiente, divide-o em até 64 bandas — menos quando o eixo é curto em píxeis do dispositivo —, avalia a função na posição paramétrica de cada banda e preenche um retângulo perpendicular ao eixo com um pincel (brush) sólido. A compensação (tradeoff) é a contagem de bandas contra a suavidade: mais bandas seguem a transição mais de perto, mas custam mais preenchimentos, sendo 64 o limite deliberado. Como a contagem também escala com o comprimento do eixo em píxeis do dispositivo, a resolução é importante. Renderizar a mesma página com um DPI mais elevado prolonga o eixo, pelo que um gradiente curto ganha bandas e as suas riscas suavizam até que o limite seja atingido
// O discretizador de sombreado usa até 64 bandas de cor ao longo do gradiente
// eixo, mas nunca mais do que o comprimento do eixo em píxeis de dispositivo. A renderização
// da mesma página com um DPI mais alto prolonga esse eixo, pelo que um gradiente curto
// ganha bandas e o efeito de banda (banding) suaviza até atingir o limite de 64 bandas.
Thumb := Pdf.RenderLoadedPageToBitmap(PageIndex, 96); // bandas mais grossas
Proof := Pdf.RenderLoadedPageToBitmap(PageIndex, 300); // até 64 bandas
Renderizar uma página sombreada para um bitmap
O ponto de entrada público permanece inalterado: o RenderLoadedPageToBitmap recebe um índice de página baseado em zero e um valor de DPI, devolve um bitmap de 24 bits pertencente ao chamador e retorna nil em caso de falha em vez de gerar uma exceção, de modo que o suporte a sombreado é transparente para o código que já possui. Uma página que costumava renderizar o seu cabeçalho em gradiente em branco agora devolve o mesmo bitmap com a transição pintada, sem ser necessária qualquer alteração na API para o obter. A única coisa a reter é a propriedade: o chamador liberta (frees) o bitmap devolvido. O pipeline completo de renderização — interpretação de fluxo de conteúdo, glifos incorporados e cache de páginas — é abordado no guia para renderizar páginas PDF para um bitmap no Delphi
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('quarterly-report.pdf') > 0 then
begin
// Página 1 (baseada em 0) a 150 DPI. Um cabeçalho preenchido com gradiente que
// costumava retornar em branco contém agora a sua transição de cores.
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
Bmp.SaveToFile('page1.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Um teste rápido de regressão obtém-se através da mesma chamada. Renderize a página, faça a amostragem de um píxel onde a banda de gradiente deve estar e confirme que já não é o branco de papel que o renderizador costumava utilizar como alternativa — uma verificação de uma única linha que deteta um retorno silencioso ao antigo comportamento de operação nula (no-op)
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
// True se o operador sh pintou; False se a região voltou a branco.
HeaderPainted := Bmp.Canvas.Pixels[Bmp.Width div 2, 8] <> clWhite;
finally
Bmp.Free;
end;
O que o HotPDF não pinta: sombreados em malha e arestas de recorte
Os quatro tipos de sombreado em malha (mesh shading) estão fora do âmbito, e os utilizadores devem saber isso antecipadamente. As malhas triangulares Gouraud de forma livre (Tipo 4), as malhas Gouraud em forma de rede (lattice) (Tipo 5), as malhas de remendo (patch) Coons (Tipo 6) e as malhas de remendo de produto tensorial (Tipo 7), definidas na norma ISO 32000-1 §8.7.4.5.5 a §8.7.4.5.8, não são renderizadas; uma página que utilize uma destas malhas continuará a ser renderizada, sendo essa região simplesmente deixada como está. Vale a pena indicar claramente duas limitações adicionais. Os retângulos de banda cobrem a faixa de sombreado em vez de serem cortados exatamente no caminho de recorte (clip path) atual, pelo que um gradiente mascarado numa forma arredondada ou irregular é pintado como a sua faixa delimitadora até que o recorte de região preciso chegue numa fase posterior. O HotPDF também assume que a saída da função é RGB ou escala de cinzentos, pelo que um sombreado criado em CMYK ou num espaço de cor exótico é aproximado em vez de gerido por cores. Para os documentos que a maioria das equipas de Delphi e C++Builder realmente processa — relatórios, faturas, extratos e apresentações exportadas para PDF —, os tipos axial e radial cobrem essencialmente todos os gradientes que irá encontrar, devendo as provas de design com muitas malhas ser tratadas como uma pré-visualização e não como uma prova final
O renderizador de páginas do HotPDF, o suporte ao operador sh aqui descrito e o avaliador de funções do Tipo 0/2/3 subjacente são fornecidos como parte do HotPDF Component para Delphi e C++Builder — uma biblioteca VCL nativa sem dependências de DLLs externas, abrangendo a criação, edição, extração de texto e renderização de páginas de PDF num único pacote