HotPDF importuje soubor SVG do načteného nebo vytvářeného PDF jako opakovaně použitelný Form XObject pomocí ImportSVGFormXObject, která vrací název zdroje vytvořeného formuláře. Vykreslení je samostatné volání, DrawSVGFormXObject, takže jeden import lze otisknout na mnoho stránek v různých velikostech, aniž by se zdvojil jediný bajt grafiky
Toto rozdělení má větší význam, než se zdá. Logo rastrované při 300 DPI a vložené na 400 stránek představuje 400 kopií bitmapy. Stejné logo jako Form XObject je jeden objekt odkazovaný 400krát, zůstává ostré při libovolném přiblížení a tiskne se v rozlišení zařízení, nikoli v DPI, které se v době tvorby zdálo dostatečné
Proč Form XObject místo opakovaného kreslení SVG?
Form XObject je vlastní mechanismus PDF pro opakovaně použitelný obsah: samostatný obsahový stream s vlastním ohraničujícím rámečkem a slovníkem zdrojů, vyvolávaný podle názvu. Import SVG do něj vám dává tři vlastnosti, které přímé kreslení nemá. Grafika existuje v souboru pouze jednou. Každé umístění je jen malá matice a jedno volání. A tvary zůstávají vektory, takže prohlížeč při přiblížení na 800 procent kreslí křivky, ne pixely
Háček je ve zdrojích. Fonty a stavy grafiky formuláře musí žít ve vlastním slovníku /Resources formuláře, nikoli ve zdrojích té stránky, která byla zrovna aktuální v době importu. HotPDF zapisuje fonty Base 14 a položky ExtGState, které formulář potřebuje, přímo do formuláře samotného, protože formulář, který si vypůjčí název od své první hostitelské stránky, se na ní vykreslí správně, ale na každé další stránce, kam je později umístěn, vyprodukuje nevyřešený název zdroje
Dva převraty: jeden pro geometrii, jeden pro glyfy
Souřadnicový systém SVG roste směrem dolů od levého horního rohu; Form XObject v PDF používá uživatelský prostor PDF, který roste směrem nahoru od levého dolního rohu. Transformace kořenového viewBox provádí jedno překlopení osy Y, aby geometrie skončila na správném místě. Text pak potřebuje druhé, lokální překlopení, protože glyf vykreslený v již překlopeném rámci by byl vzhůru nohama
Jde o stejnou korekci, která se objevuje v opačném směru při exportu stránky PDF do SVG, popsanou v článku export stránek PDF do SVG. Chyba v této korekci je snadno vidět u grafiky s textem a téměř neviditelná u grafiky bez textu, a proto je logo s podtitulkem lepším testem než logo samotné
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');
// Jeden import, mnoho umístění: bajty grafiky existují jen jednou
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;
Jak hypertextové odkazy přežijí cestu?
Uvnitř formuláře přežít nemohou, protože Form XObject je obsah, zatímco cíle odkazů v PDF jsou anotace, které patří stránce. Element <a> ve zdrojovém SVG proto nelze jednoduše zkopírovat do obsahového streamu formuláře
HotPDF to řeší tak, že obdélníky odkazů udržuje během parsování jako metadata v prostoru formuláře a při každém umístění je materializuje jako anotace odkazů na úrovni stránky. DrawSVGFormXObject mapuje tyto obdélníky přes cílový obdélník, který zadáte, takže formulář umístěný dvakrát v různých velikostech získá dvě správně dimenzované sady klikacích oblastí. Obdélníky odkazů se mapují pouze přes rozměr kořene a nikdy nevstupují do obsahového streamu, což zajišťuje, že vykreslená grafika zůstává stejná bez ohledu na počet odkazů, které nese. Předejte AddLinks = False, když chcete grafiku bez interaktivity, například na stránce určené jen pro tisk
Co importér odmítá a proč je to záměr
SVG je dialekt XML se skriptováním, externími odkazy a expanzí entit, což činí libovolný soubor SVG stejně nebezpečným vstupem jako libovolný soubor HTML. Importní profil se u všeho, co míří mimo dokument, chová podle zásady fail-closed: externí odkazy na zdroje, skripty, atributy obsluhy událostí, DTD a jakékoli url(), které není lokálním odkazem na fragment
Fail-closed znamená, že import selže, místo aby závadnou konstrukci tiše zahodil. Tento rozdíl má význam pro dávkové zpracování, protože tiché zahození vytvoří dokument, kterému něco chybí, aniž by to kdokoli tušil, zatímco odmítnutí vytvoří soubor, který lze izolovat a prozkoumat. Pokud přijímáte nahrávání SVG od uživatelů, je toto přesně chování, které chcete, a doplňuje izolaci na úrovni procesu popsanou v článku izolace kodeků na úrovni procesu pro nedůvěryhodné obrázky
Stojí za zmínku jedna interní hranice, pokud vykreslujete i barevné fonty. Přísná podmnožina SVG používaná pro barevné glyfy OpenType SVG prochází samostatným vstupním bodem a zachovává si své původní, užší chování při selhání. Obecný import grafiky je oddělená cesta, takže přidání podpory oblouků, průhlednosti a CSS do importované grafiky nerozšířilo to, co smí obsahovat glyf fontu
Přechody, masky a pravidlo bez rastrování
Přechody se importují jako ohraničené vektorové barevné pásy omezené ořezovou cestou původní geometrie, nikoli jako rastrovaný obrázek. Masky používají stejný přístup: obrys masky se stane vektorovou ořezovou cestou. Výsledkem je, že ilustrace bohatá na přechody zůstává nezávislá na rozlišení a zůstává malá, zatímco rastrující importér by vytvořil velký obrazový objekt, jehož kvalita je pevně daná v okamžiku importu
Cenou je věrnost v extrémních případech. Pásová aproximace hladkého přechodu není matematicky totožná s přechodem SVG a grafika s desítkami překrývajících se měkkých masek bude blíže originálu, pokud ji místo toho vykreslíte do obrázku s vysokým rozlišením. Pro modely stínování, které PDF podporuje nativně, a kdy po nich sáhnout přímo, viz článek vykreslování axiálních a radiálních stínování
// Čtení statistik importu vám řekne, co se skutečně dostalo dovnitř
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]));
// Umístění stejného formuláře ve dvou velikostech vytvoří dvě správné sady odkazů
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);
// Umístění jen pro tisk: grafika bez klikacích oblastí
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);
Kam import SVG zapadá vedle ostatních vektorových cest
Existují tři cesty pro vektorový import a nejsou zaměnitelné. Import SVG je vhodný pro grafiku vytvořenou nástroji pro design a pro cokoli, co přichází z webového pipeline. Import metasouborů, popsaný v článku vektorový import EMF a WMF, je vhodný pro obsah generovaný aplikacemi Windows a komponentami pro grafy, které kreslí do kontextu zařízení. Přímé kreslení přes API stránky je vhodné pro grafiku, kterou počítá váš vlastní kód, kde neexistuje žádný mezistupňový formát
Argument opakovatelnosti hovoří ve prospěch importu SVG konkrétně pro opakující se prvky. Hlavičkové papíry, loga, vodoznaky, certifikační značky a dekorativní linky se všechny kreslí identicky na mnoha stránkách a každý z nich je jeden objekt formuláře plus jedno volání na stránku. Rozdíl ve velikosti souboru změřte na reálném dokumentu, než usoudíte, že na tom nezáleží: u 500stránkového katalogu s vektorovým logem na každé stránce bývá rozdíl mezi jedním formulářem a 500 vloženými kopiemi obvykle několik megabajtů
Import SVG, import metasouborů a přímé vektorové kreslení sdílejí pro Delphi a C++Builder stejný zapisovač obsahového streamu; úplný seznam funkcí najdete na stránce komponenty HotPDF Delphi PDF component