Artigo Técnico

Máscaras suaves e achatamento de transparência no Delphi

A PDF Library for Delphi cria os dois tipos de máscara suave de PDF a partir do Delphi. SetImageSoftMaskEx anexa uma imagem de máscara a uma imagem alvo e pode gravar a entrada /Matte que a arte pré-multiplicada exige. NewSoftMaskGraphicsState constrói uma máscara alfa ou de luminosidade a partir de conteúdo capturado e retorna um graphics state que você aplica ao desenho subsequente. E quando um consumidor não consegue lidar com transparência de forma alguma, FlattenTransparency renderiza o resultado visual e substitui cada página por ele

Máscaras suaves são como o PDF expressa um degradê, uma vinheta, uma foto mesclada em um fundo, e todo outro efeito em que a opacidade varia através de uma região. Transparência constante é um único número no graphics state; uma máscara suave é uma função inteira de posição, e até você conseguir criar uma, uma categoria inteira de design não pode ser reproduzida

Os dois tipos de máscara, e o que cada um lê

Uma máscara alfa tira seus valores do canal alfa do conteúdo de mascaramento. Uma máscara de luminosidade os tira do brilho do conteúdo de mascaramento, de modo que uma área branca é totalmente opaca e uma área preta totalmente transparente. Luminosidade é a que os designers usam, porque pintar um degradê em tons de cinza é uma forma natural de descrever um esmaecimento

Máscaras suaves de formulário são construídas a partir de conteúdo capturado, e vários detalhes são fixos, e não opcionais. O conteúdo capturado se torna um grupo de transparência isolado em DeviceRGB, de modo que a máscara é avaliada em um espaço de cor conhecido. A cor de fundo, portanto, precisa ter três componentes, correspondendo a esse grupo. A função de transferência aceita /Identity ou um objeto de função válido, permitindo que você remodele a curva da máscara sem repintar a arte

Diagrama comparando máscaras suaves de luminosidade e alfa alimentando NewSoftMaskGraphicsState, com um lembrete de que o graphics state persiste até ser limpo
Máscaras de luminosidade leem o brilho, enquanto máscaras alfa leem a cobertura, e NewSoftMaskGraphicsState envolve qualquer uma delas em um graphics state persistente
uses
  PDFlibrary;

var
  Lib: TPDFlib;
  MaskCapture, MaskGS, PhotoID: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('brochure.pdf', '');

    // A página 4 tem um degradê em tons de cinza pintado para uso como máscara
    MaskCapture := Lib.CapturePage(4);

    MaskGS := Lib.NewSoftMaskGraphicsState(MaskCapture,
      PDF_SOFT_MASK_LUMINOSITY, '0 0 0', '/Identity');

    Lib.SelectPage(1);
    Lib.SetGraphicsState(MaskGS);
    PhotoID := Lib.AddImageFromFile('hero.jpg', 0);
    Lib.SelectImage(PhotoID);
    Lib.DrawImage(40, 700, 515, 260);        // esmaece sob a máscara

    // Os parâmetros de ExtGState persistem: encerre o escopo explicitamente
    Lib.SetGraphicsState(Lib.NewSoftMaskGraphicsState(0,
      PDF_SOFT_MASK_NONE, '', ''));

    Lib.SaveToFile('brochure-masked.pdf');
  finally
    Lib.Free;
  end;
end;

Essa última etapa não é decoração. Os parâmetros de graphics state persistem até serem alterados, e aplicar um graphics state comum depois não limpa uma máscara suave ativa. Sem um /SMask /None explícito, tudo o que é desenhado depois da arte mascarada herda a máscara, o que é um dos bugs mais desconcertantes de diagnosticar visualmente

Máscaras de imagem e a entrada Matte

Para mascaramento em nível de imagem, a imagem de máscara é normalizada primeiro para DeviceGray, de modo que uma máscara fornecida em qualquer espaço de cor se comporta de forma previsível. A entrada opcional /Matte, de um a quatro componentes dependendo do espaço de cor da imagem, informa a um consumidor que os dados da imagem estão pré-multiplicados contra aquela cor, o que evita o halo escuro que aparece quando arte pré-multiplicada é composta como se fosse alfa direto

Diagrama mostrando uma entrada Matte ausente produzindo halos escuros, enquanto um Matte declarado compõe de forma limpa, ao lado das barreiras de PDF 1.4, PDF/A e PDF/X
Um /Matte declarado evita o halo escuro de arte pré-multiplicada, enquanto arquivos PDF/A-1 e PDF/X-1 são recusados antes de qualquer mudança de objeto

Duas barreiras importam aqui. PDF/A-1 e PDF/X-1 proíbem transparência, e a chamada se recusa antes de modificar qualquer objeto, em vez de produzir um arquivo que falha na validação depois. E uma máscara suave bem-sucedida exige PDF 1.4, que a biblioteca define automaticamente — transparência não existia antes dessa versão, e um cabeçalho 1.3 em um arquivo contendo máscaras suaves é simplesmente falso

Estados repetidos são reutilizados: a mesma forma capturada, tipo de máscara, cor de fundo e assinatura de função de transferência geram o mesmo objeto de graphics state, em vez de um novo por chamada. Em um documento que mascara cem imagens de forma idêntica, isso são noventa e nove objetos que nunca chegam a ser gravados

Por que achatar, e o que o achatamento realmente faz

Transparência ainda é a fonte mais comum de "parece certo na tela e errado na impressão". RIPs mais antigos, alguns sistemas de submissão regulatória e o PDF/X-1a todos exigem um arquivo sem transparência ativa alguma

O achatamento aqui é deliberadamente literal: cada página no intervalo solicitado é renderizada em um DPI escolhido sobre um fundo opaco, e o conteúdo e os recursos da página são substituídos por essa única imagem. Como o renderizador produz o resultado composto final, isso cobre tudo de uma vez — grupos de transparência, modos de mesclagem, máscaras suaves, padrões e formulários aninhados — sem nenhuma lógica de aproximação por construção que possa dar errado. A memória de raster de pico permanece limitada a uma página

Diagrama de pipeline do FlattenTransparency renderizando cada página solicitada a 300 DPI sobre um fundo opaco e salvando um arquivo PDF 1.3 achatado pronto para impressão
Cada página solicitada é renderizada uma vez no DPI escolhido e retorna como uma única imagem opaca salva em um arquivo PDF 1.3 fiel
// Achata as páginas 1-8 a 300 DPI em branco, depois salva como PDF 1.3
Lib.FlattenTransparency('1-8', 300, $FFFFFF, 0);
Lib.SaveToFile('press-ready.pdf');

O custo é igualmente literal: a saída é uma imagem, então o texto não é mais selecionável ou pesquisável, e as bordas vetoriais são reamostradas no DPI escolhido. O achatamento é uma etapa de entrega, não uma etapa de edição. Mantenha o original transparente e achate uma cópia

Páginas rotacionadas, unidades de usuário e as caixas de página

Uma página rotacionada precisa de cuidado que uma implementação ingênua pula. Depois da renderização, a largura e a altura visuais são gravadas de volta no MediaBox, a unidade de usuário é incorporada ao DPI efetivo e ao novo tamanho de página, e a entrada /Rotate, a unidade de usuário e as caixas de página auxiliares são removidas

Pular isso produz um arquivo em que a imagem achatada está correta, mas um visualizador a rotaciona de novo, ou a escala por uma unidade de usuário que não se aplica mais. Remover as caixas em vez de recalculá-las é a decisão certa, porque uma TrimBox descrevendo a geometria pré-achatamento é pior do que nenhuma TrimBox

Anotações e campos de formulário são renderizados na imagem estática por padrão, e os objetos interativos são removidos, o que é o que um material pronto para impressão quer. Se você mantiver os objetos interativos em vez disso, a passagem de anotações do renderizador é desativada para que nada seja desenhado duas vezes

A verificação de versão que escaneia tudo

Salvar como PDF 1.3 exige que nenhuma transparência reste em lugar nenhum, e a verificação pré-salvamento examina todo objeto indireto, não apenas os alcançáveis a partir das páginas. Isso é mais rigoroso do que parece à primeira vista e está correto: uma máscara suave órfã deixada por uma edição anterior ainda está no arquivo, e um consumidor que percorre objetos em vez de páginas vai encontrá-la

Para tornar o rebaixamento de versão viável, uma passagem de mark-and-sweep roda por padrão assim que toda página alvo foi substituída com sucesso, liberando os objetos inalcançáveis. A consequência a planejar é que identificadores de imagem, página capturada e graphics state obtidos antes dessa varredura podem não ser mais válidos depois. Readquira os identificadores de recurso de que você precisa antes de continuar a editar, ou desative a varredura e aceite que a versão não pode então ser rebaixada

Duas companheiras completam o quadro. Arte vetorial que usa shadings e degradês em vez de máscaras é abordada em gráficos vetoriais, shaders e degradês, e é frequentemente a rota melhor quando o efeito pode ser expresso como um shading. E se o motivo pelo qual você está achatando é uma exigência de cor, e não de transparência, o caminho de conversão em recolorindo um documento para outro espaço de cor preserva o texto ativo onde o achatamento não preservaria

A criação de máscaras suaves, o achatamento e o motor de renderização por trás deles vêm em uma única biblioteca para Delphi, C++Builder e Free Pascal; a lista completa de recursos está na página da PDF Library for Delphi