HotPDF importerar en SVG-fil till ett inläst eller nyskapat PDF-dokument som ett återanvändbart Form XObject via ImportSVGFormXObject, som returnerar resursnamnet för det skapade formuläret. Att placera ut det är ett separat anrop, DrawSVGFormXObject, så en enda import kan stämplas på många sidor i olika storlekar utan att en enda byte av grafiken dupliceras
Denna uppdelning spelar större roll än det låter som. En logotyp rastrerad vid 300 DPI och infogad på 400 sidor blir 400 kopior av en bitmapp. Samma logotyp som ett Form XObject är ett enda objekt som refereras 400 gånger, den förblir skarp vid vilken zoomnivå som helst, och den skrivs ut i enhetens upplösning snarare än vid vilken DPI som verkade tillräcklig vid byggtillfället
Varför ett Form XObject istället för att rita SVG:n varje gång?
Ett Form XObject är PDF:s egen mekanism för återanvändbart innehåll: en självständig innehållsström med sin egen avgränsningsruta och resursordlista, som anropas via namn. Att importera SVG till ett sådant ger tre egenskaper som direkt ritning inte ger. Grafiken finns bara en gång i filen. Varje placering är en liten matris och ett anrop. Och formerna förblir vektorer, så en visare vid 800 procents zoom ritar kurvor, inte pixlar
Det subtila ligger i resurserna. Ett formulärs typsnitt och grafiktillstånd måste finnas i formulärets egen /Resources-ordlista, inte i resurserna för vilken sida som råkade vara aktuell vid importtillfället. HotPDF skriver de Base 14-typsnitt och ExtGState-poster som ett formulär behöver direkt in i formuläret, eftersom ett formulär som lånar ett namn från sin första värdsida renderas korrekt där men ger ett olöst resursnamn på varje annan sida det senare placeras på
Två vändningar, en för geometrin och en för glyferna
SVG:s koordinatsystem växer nedåt från övre vänstra hörnet; ett PDF Form XObject använder PDF:s användarrymd, som växer uppåt från nedre vänstra hörnet. Roten viewBox-transformen utför en vändning av Y-axeln så att geometrin hamnar rätt. Text behöver därefter en andra, lokal vändning, eftersom en glyf ritad i en redan vänd ram annars skulle hamna upp och ner
Detta är samma korrigering som uppträder i omvänd riktning när en PDF-sida skrivs ut som SVG, beskrivet i export av PDF-sidor till SVG. Att göra fel här är lätt att upptäcka i grafik med text och nästan osynligt i grafik utan, vilket är varför en logotyp med slogan är ett bättre röktest än en logotyp ensam
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');
// En import, många placeringar: grafikens byte finns bara en gång
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;
Hur överlever hyperlänkarna resan?
De kan inte överleva inuti formuläret, eftersom ett Form XObject är innehåll medan PDF-länkmål är annoteringar, som hör till en sida. Ett <a>-element i käll-SVG:n kan därför inte helt enkelt kopieras in i formulärets innehållsström
HotPDF löser detta genom att behålla länkrektanglar som metadata i formulärets rymd under parsning, och materialisera dem som länkannoteringar på sidnivå vid varje placering. DrawSVGFormXObject mappar dessa rektanglar genom målrektangeln du anger, så ett formulär som placeras två gånger i olika storlekar får två korrekt dimensionerade uppsättningar klickbara ytor. Länkrektanglar mappas endast genom rotstorleken och kommer aldrig in i innehållsströmmen, vilket håller den ritade grafiken identisk oavsett hur många länkar den bär. Ange AddLinks = False när du vill ha grafiken utan interaktiviteten, till exempel på en sida avsedd enbart för utskrift
Vad importören avvisar, och varför det är poängen
SVG är en XML-dialekt med skriptstöd, externa referenser och entitetsexpansion, vilket gör en godtycklig SVG-fil till en lika fientlig indata som en godtycklig HTML-fil. Importprofilen är fail-closed på allt som sträcker sig utanför dokumentet: externa resursreferenser, skript, händelsehanterarattribut, DTD:er och varje url() som inte är en lokal fragmentreferens
Fail-closed betyder att importen misslyckas istället för att tyst släppa den felaktiga konstruktionen. Den skillnaden spelar roll för en batch-pipeline, eftersom ett tyst bortfall ger ett dokument som saknar något som ingen känner till, medan en avvisning ger en fil du kan karantänsätta och granska. Om du tar emot SVG-uppladdningar från användare är detta det beteende du vill ha, och det går hand i hand med den processnivåisolering som beskrivs i processisolering för kodekar med opålitliga bilder
En intern gräns är värd att känna till om du också renderar färgtypsnitt. Den strikta SVG-delmängd som används för OpenType SVG-färgglyfer går genom en separat ingångspunkt och behåller sitt ursprungliga, snävare felbeteende. Allmän grafikimport är en egen väg, så att lägga till stöd för bågar, transparens och CSS för importerad grafik utökade inte vad en typsnittsglyf tillåts innehålla
Gradienter, masker och regeln mot rastrering
Gradienter importeras som avgränsade vektorfärgband, begränsade av den ursprungliga geometrins klippbana, inte som en rastrerad bild. Masker använder samma metod: maskens kontur blir en vektorklippbana. Resultatet är att en gradientrik illustration förblir upplösningsoberoende och liten, medan en rastrerande importör skulle producera ett stort bildobjekt vars kvalitet fastställs vid importtillfället
Priset är precision i extremfallen. En bandad approximation av en jämn gradient är inte matematiskt identisk med SVG-gradienten, och grafik med dussintals överlappande mjuka masker kommer att se mer likt originalet ut om du istället renderar den till en högupplöst bild. För de skuggningsmodeller PDF stöder inbyggt, och när det lönar sig att använda dem direkt, se rendering av axiella och radiella skuggningar
// Att läsa importstatistiken visar vad som faktiskt kom igenom
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]));
// Att placera samma formulär i två storlekar ger två korrekta länkuppsättningar
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);
// Placering enbart för utskrift: grafik utan klickbara ytor
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);
Var SVG-import passar bland de andra vektorvägarna
Det finns tre vägar för vektorimport och de är inte utbytbara mot varandra. SVG-import är rätt för grafik som producerats av designverktyg och för allt som kommer från en webbpipeline. Metafilimport, som beskrivs i EMF- och WMF-vektorimport, är rätt för innehåll som genereras av Windows-applikationer och av diagramkomponenter som ritar till en enhetskontext. Direkt ritning via sid-API:et är rätt för grafik din egen kod beräknar, där det inte finns något mellanformat alls
Återanvändbarhetsargumentet talar specifikt för SVG-import när det gäller upprepade element. Brevhuvuden, logotyper, vattenstämplar, certifieringsmärken och dekorativa linjer ritas alla identiskt på många sidor, och var och en av dem är ett formulärobjekt plus ett anrop per sida. Mät filstorleksskillnaden på ett verkligt dokument innan du bestämmer att det inte spelar någon roll: i en katalog på 500 sidor med en vektorlogotyp per sida är skillnaden mellan ett formulär och 500 inbäddade kopior vanligtvis flera megabyte
SVG-import, metafilimport och direkt vektorritning delar samma innehållsströmskrivare för Delphi och C++Builder; den fullständiga funktionslistan finns på HotPDF Delphi PDF-komponentsidan