O HotPDF importa um arquivo SVG para um PDF carregado ou criado como um Form XObject reutilizável por meio de ImportSVGFormXObject, que retorna o nome do recurso do formulário criado. Posicioná-lo é uma chamada separada, DrawSVGFormXObject, de modo que uma única importação pode ser estampada em muitas páginas em tamanhos diferentes sem duplicar um único byte da arte
Essa separação importa mais do que parece. Um logotipo rasterizado a 300 DPI e colocado em 400 páginas são 400 cópias de um bitmap. O mesmo logotipo como Form XObject é um único objeto referenciado 400 vezes, permanece nítido em qualquer nível de zoom e imprime na resolução do dispositivo, e não no DPI que parecia suficiente no momento da compilação
Por que usar um Form XObject em vez de desenhar o SVG a cada vez?
Um Form XObject é o próprio mecanismo do PDF para conteúdo reutilizável: um fluxo de conteúdo autocontido com sua própria caixa delimitadora e dicionário de recursos, invocado pelo nome. Importar um SVG para um deles oferece três propriedades que o desenho direto não oferece. A arte existe uma única vez no arquivo. Cada posicionamento é uma pequena matriz e uma invocação. E as formas continuam sendo vetores, de modo que um visualizador em zoom de 800 por cento desenha curvas, não pixels
O detalhe sutil está nos recursos. As fontes e os estados gráficos de um formulário devem residir no próprio dicionário /Resources do formulário, não nos recursos da página que por acaso estava ativa no momento da importação. O HotPDF grava as fontes Base 14 e as entradas ExtGState que um formulário precisa dentro do próprio formulário, porque um formulário que toma emprestado um nome de sua primeira página hospedeira renderiza corretamente ali e produz um nome de recurso não resolvido em qualquer outra página em que for posicionado depois
Duas inversões: uma para a geometria e outra para os glifos
O sistema de coordenadas do SVG cresce para baixo a partir do canto superior esquerdo; um Form XObject de PDF usa o espaço do usuário do PDF, que cresce para cima a partir do canto inferior esquerdo. A transformação do viewBox raiz realiza uma inversão do eixo Y para que a geometria fique posicionada corretamente. O texto então precisa de uma segunda inversão, local, porque um glifo desenhado em um quadro já invertido ficaria de cabeça para baixo
Essa é a mesma correção que aparece de forma inversa quando uma página PDF é exportada como SVG, descrita em exportar páginas PDF para SVG. Errar isso é fácil de perceber em arte com texto e quase invisível em arte sem texto, razão pela qual um logotipo com slogan é um teste de fumaça melhor do que um logotipo sozinho
uses
HPDFDoc;
var
Pdf: THotPDF;
SvgBytes: TBytes;
FormName: AnsiString;
Stats: THPDFSVGImportStatistics;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'catalogue.pdf';
Pdf.BeginDoc;
SvgBytes := LoadFileBytes('brand-logo.svg');
FormName := Pdf.ImportSVGFormXObject(SvgBytes, Stats);
if FormName = '' then
raise Exception.Create('SVG rejected - see the import statistics');
// Uma importação, muitos posicionamentos: os bytes da arte existem uma única vez
for I := 1 to 40 do
begin
if I > 1 then
Pdf.AddPage;
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 780, 120, 40);
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Como os hiperlinks sobrevivem à viagem?
Eles não conseguem sobreviver dentro do formulário, porque um Form XObject é conteúdo e os destinos de link do PDF são anotações, que pertencem a uma página. Um elemento <a> no SVG de origem, portanto, não pode simplesmente ser copiado para o fluxo de conteúdo do formulário
O HotPDF resolve isso mantendo os retângulos de link como metadados do espaço do formulário durante a análise, e materializando-os como anotações de link em nível de página a cada posicionamento. DrawSVGFormXObject mapeia esses retângulos por meio do retângulo de destino que você passa, de modo que um formulário posicionado duas vezes em tamanhos diferentes recebe dois conjuntos de áreas de clique corretamente dimensionados. Os retângulos de link são mapeados apenas por meio do tamanho raiz e nunca entram no fluxo de conteúdo, o que mantém a arte desenhada idêntica independentemente de quantos links ela carrega. Passe AddLinks = False quando quiser a arte sem a interatividade, por exemplo em uma página apenas para impressão
O que o importador rejeita, e por que isso é o ponto central
SVG é um dialeto XML com scripting, referências externas e expansão de entidades, o que torna um arquivo SVG arbitrário uma entrada hostil da mesma forma que um arquivo HTML arbitrário é. O perfil de importação é fail-closed para tudo que alcança fora do documento: referências a recursos externos, scripts, atributos de manipuladores de eventos, DTDs e qualquer url() que não seja uma referência de fragmento local
Fail-closed significa que a importação falha em vez de descartar silenciosamente a construção problemática. Essa distinção importa para um pipeline em lote, porque um descarte silencioso produz um documento ao qual falta algo que ninguém sabe, enquanto uma rejeição produz um arquivo que você pode colocar em quarentena e inspecionar. Se você aceita uploads de SVG de usuários, esse é o comportamento que você quer, e ele combina com o isolamento em nível de processo descrito em isolamento de processo de codec para imagens não confiáveis
Vale a pena conhecer um limite interno se você também renderiza fontes coloridas. O subconjunto estrito de SVG usado para glifos coloridos OpenType SVG passa por um ponto de entrada separado e mantém seu comportamento de falha original, mais restrito. A importação geral de arte é um caminho distinto, então adicionar suporte a arcos, transparência e CSS à arte importada não ampliou o que um glifo de fonte pode conter
Gradientes, máscaras e a regra de não rasterizar
Os gradientes são importados como faixas de cor vetoriais delimitadas, restritas pelo caminho de recorte da geometria original, e não como uma imagem rasterizada. As máscaras usam a mesma abordagem: o contorno da máscara se torna um caminho de recorte vetorial. O resultado é que uma ilustração cheia de gradientes permanece independente de resolução e permanece pequena, enquanto um importador que rasteriza produziria um grande objeto de imagem cuja qualidade fica fixada no momento da importação
O custo é a fidelidade nos casos extremos. Uma aproximação em faixas de um gradiente suave não é matematicamente idêntica ao gradiente SVG, e arte com dezenas de máscaras suaves sobrepostas ficará mais próxima do original se você a renderizar como uma imagem de alta resolução. Para os modelos de sombreamento que o PDF suporta nativamente, e quando recorrer a eles diretamente, veja renderizando sombreamentos axiais e radiais
// Ler as estatísticas de importação mostra o que realmente passou
Writeln(Format('nodes=%d css rules=%d gradients=%d masks=%d',
[Stats.NodeCount, Stats.CSSRuleCount, Stats.GradientCount,
Stats.MaskCount]));
Writeln(Format('text runs=%d links=%d form bytes=%d',
[Stats.TextRunCount, Stats.LinkCount, Stats.OutputBytes]));
// Posicionar o mesmo formulário em dois tamanhos produz dois conjuntos de links corretos
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);
// Posicionamento apenas para impressão: arte sem as áreas de clique
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);
Onde a importação de SVG se encaixa ao lado das outras rotas vetoriais
Existem três rotas de importação vetorial e elas não são intercambiáveis. A importação de SVG é adequada para arte produzida por ferramentas de design e para qualquer coisa que chegue de um pipeline web. A importação de metarquivos, abordada em importação vetorial de EMF e WMF, é adequada para conteúdo gerado por aplicativos Windows e por componentes de gráficos que desenham em um contexto de dispositivo. O desenho direto por meio da API de página é adequado para arte que seu próprio código calcula, onde não existe formato intermediário algum
O argumento da reutilização favorece a importação de SVG especificamente para elementos repetidos. Papéis timbrados, logotipos, marcas d'água, selos de certificação e regras decorativas são todos desenhados de forma idêntica em muitas páginas, e cada um deles é um objeto de formulário mais uma invocação por página. Meça a diferença de tamanho de arquivo em um documento real antes de decidir que isso não importa: em um catálogo de 500 páginas com um logotipo vetorial por página, a diferença entre um formulário e 500 cópias embutidas costuma ser de vários megabytes
A importação de SVG, a importação de metarquivos e o desenho vetorial direto compartilham o mesmo gravador de fluxo de conteúdo para Delphi e C++Builder; a lista completa de recursos está na página do componente PDF Delphi HotPDF