HotPDF importiert eine SVG-Datei über ImportSVGFormXObject als wiederverwendbares Form-XObject in ein geladenes oder neu erstelltes PDF; die Methode liefert den Ressourcennamen des erzeugten Formulars zurück. Das Platzieren erfolgt über einen separaten Aufruf, DrawSVGFormXObject, sodass ein einziger Import auf vielen Seiten in unterschiedlichen Größen aufgebracht werden kann, ohne auch nur ein einziges Byte der Grafik zu duplizieren
Diese Trennung ist wichtiger, als es klingt. Ein mit 300 DPI gerastertes Logo, das auf 400 Seiten platziert wird, ergibt 400 Kopien einer Bitmap. Dasselbe Logo als Form-XObject ist ein einziges Objekt, das 400-mal referenziert wird, bleibt bei jedem Zoomfaktor scharf und wird in Geräteauflösung gedruckt statt in der DPI-Zahl, die zum Erstellungszeitpunkt ausreichend schien
Warum ein Form-XObject statt das SVG jedes Mal neu zu zeichnen?
Ein Form-XObject ist PDFs eigener Mechanismus für wiederverwendbare Inhalte: ein in sich geschlossener Content-Stream mit eigener Bounding Box und eigenem Ressourcen-Dictionary, der über einen Namen aufgerufen wird. Der Import von SVG in ein solches Objekt liefert drei Eigenschaften, die direktes Zeichnen nicht bietet. Die Grafik existiert nur einmal in der Datei. Jede Platzierung besteht lediglich aus einer kleinen Matrix und einem Aufruf. Und die Formen bleiben Vektoren, sodass ein Betrachter bei 800 Prozent Zoom Kurven zeichnet, keine Pixel
Die Feinheit liegt bei den Ressourcen. Die Schriften und Grafikzustände eines Formulars müssen im eigenen /Resources-Dictionary des Formulars liegen, nicht in den Ressourcen der Seite, die zum Importzeitpunkt gerade aktuell war. HotPDF schreibt die Base-14-Schriften und die vom Formular benötigten ExtGState-Einträge direkt in das Formular selbst, denn ein Formular, das einen Namen von seiner ersten Host-Seite entlehnt, wird dort zwar korrekt gerendert, erzeugt aber auf jeder anderen Seite, auf der es später platziert wird, einen nicht auflösbaren Ressourcennamen
Zwei Spiegelungen, eine für die Geometrie und eine für die Glyphen
Das Koordinatensystem von SVG wächst von der oberen linken Ecke nach unten; ein PDF-Form-XObject verwendet den PDF-Anwenderraum, der von unten links nach oben wächst. Die viewBox-Transformation an der Wurzel führt eine Y-Achsen-Spiegelung durch, damit die Geometrie korrekt landet. Text benötigt anschließend eine zweite, lokale Spiegelung, denn eine in einem bereits gespiegelten Rahmen gezeichnete Glyphe stünde sonst auf dem Kopf
Es handelt sich um dieselbe Korrektur, die in umgekehrter Richtung auftritt, wenn eine PDF-Seite als SVG exportiert wird, beschrieben unter Export von PDF-Seiten nach SVG. Ein Fehler dabei ist bei Grafiken mit Text leicht zu erkennen und bei Grafiken ohne Text fast unsichtbar, weshalb ein Logo mit Slogan der bessere Testfall ist als ein Logo allein
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');
// Ein Import, viele Platzierungen: die Grafik-Bytes existieren nur einmal
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;
Wie überstehen Hyperlinks den Import?
Innerhalb des Formulars können sie das nicht, denn ein Form-XObject ist Inhalt, während PDF-Linkziele Annotationen sind, die zu einer Seite gehören. Ein <a>-Element im Quell-SVG kann daher nicht einfach in den Content-Stream des Formulars kopiert werden
HotPDF löst dies, indem Link-Rechtecke während des Parsens als Metadaten im Formularraum gehalten und bei jeder Platzierung als seitenbezogene Link-Annotationen materialisiert werden. DrawSVGFormXObject bildet diese Rechtecke über das von Ihnen übergebene Zielrechteck ab, sodass ein zweimal in unterschiedlichen Größen platziertes Formular zwei korrekt bemessene Trefferbereiche erhält. Link-Rechtecke werden nur über die Wurzelgröße abgebildet und gelangen nie in den Content-Stream, wodurch die gezeichnete Grafik unabhängig von der Anzahl der Links identisch bleibt. Übergeben Sie AddLinks = False, wenn Sie die Grafik ohne die Interaktivität wünschen, etwa auf einer reinen Druckseite
Was der Importer verweigert, und warum das der Sinn der Sache ist
SVG ist ein XML-Dialekt mit Skripting, externen Referenzen und Entity-Expansion, was eine beliebige SVG-Datei ebenso zu einer potenziell feindlichen Eingabe macht wie eine beliebige HTML-Datei. Das Importprofil verhält sich bei allem, was über das Dokument hinausreicht, fail-closed: externe Ressourcenreferenzen, Skripte, Event-Handler-Attribute, DTDs und jedes url(), das keine lokale Fragmentreferenz ist
Fail-closed bedeutet, dass der Import fehlschlägt, statt das betroffene Konstrukt stillschweigend zu verwerfen. Diese Unterscheidung ist für eine Batch-Pipeline wichtig, denn ein stilles Verwerfen erzeugt ein Dokument, dem etwas fehlt, ohne dass jemand davon weiß, während eine Verweigerung eine Datei liefert, die Sie unter Quarantäne stellen und untersuchen können. Wenn Sie SVG-Uploads von Benutzern akzeptieren, ist dies das gewünschte Verhalten, und es passt zur Prozessisolation, die unter Codec-Prozessisolation für nicht vertrauenswürdige Bilder beschrieben ist
Eine interne Grenze ist wissenswert, falls Sie auch Farbschriften rendern. Die strikte SVG-Teilmenge für OpenType-SVG-Farbglyphen läuft über einen separaten Einstiegspunkt und behält ihr ursprüngliches, engeres Fehlerverhalten bei. Der allgemeine Grafikimport ist ein eigenständiger Pfad, sodass die Erweiterung des Grafikimports um Bögen, Transparenz und CSS-Unterstützung nicht erweitert hat, was eine Schriftglyphe enthalten darf
Verläufe, Masken und die Nicht-Raster-Regel
Verläufe werden als begrenzte vektorielle Farbbänder importiert, eingeschränkt durch den Clipping-Pfad der ursprünglichen Geometrie, nicht als gerastertes Bild. Masken folgen demselben Ansatz: Die Maskenkontur wird zu einem vektoriellen Clipping-Pfad. Das Ergebnis ist, dass eine verlaufsreiche Illustration auflösungsunabhängig und klein bleibt, während ein rasternder Importer ein großes Bildobjekt erzeugen würde, dessen Qualität zum Importzeitpunkt festgelegt ist
Der Kompromiss liegt in der Genauigkeit bei Extremfällen. Eine bandartige Annäherung an einen glatten Verlauf ist mathematisch nicht identisch mit dem SVG-Verlauf, und eine Grafik mit Dutzenden überlappender weicher Masken sieht dem Original näher, wenn Sie sie stattdessen in ein hochaufgelöstes Bild rendern. Für die Shading-Modelle, die PDF nativ unterstützt, und wann Sie direkt darauf zurückgreifen sollten, siehe Rendern axialer und radialer Shadings
// Das Auslesen der Importstatistik zeigt, was tatsächlich übernommen wurde
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]));
// Das Platzieren desselben Formulars in zwei Größen erzeugt zwei korrekte Link-Sätze
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);
// Nur für den Druck: Grafik ohne Trefferbereiche
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);
Wo sich der SVG-Import neben den anderen Vektorwegen einordnet
Es gibt drei Wege für den Vektorimport, die nicht austauschbar sind. Der SVG-Import eignet sich für Grafiken aus Design-Tools und für alles, was aus einer Web-Pipeline kommt. Der Metadatei-Import, behandelt unter EMF- und WMF-Vektorimport, eignet sich für Inhalte, die von Windows-Anwendungen und von Diagrammkomponenten erzeugt werden, die auf einen Gerätekontext zeichnen. Direktes Zeichnen über die Seiten-API eignet sich für Grafiken, die Ihr eigener Code berechnet, wo es überhaupt kein Zwischenformat gibt
Das Argument der Wiederverwendbarkeit spricht speziell bei wiederkehrenden Elementen für den SVG-Import. Briefköpfe, Logos, Wasserzeichen, Zertifizierungszeichen und dekorative Linien werden alle identisch auf vielen Seiten gezeichnet, und jedes davon ist ein Formularobjekt plus ein Aufruf pro Seite. Messen Sie den Dateigrößenunterschied an einem echten Dokument, bevor Sie entscheiden, dass es keine Rolle spielt: Bei einem 500-seitigen Katalog mit einem Vektorlogo pro Seite beträgt der Unterschied zwischen einem Formular und 500 Inline-Kopien meist mehrere Megabyte
SVG-Import, Metadatei-Import und direktes Vektorzeichnen teilen sich denselben Content-Stream-Writer für Delphi und C++Builder; die vollständige Funktionsliste finden Sie auf der HotPDF-Delphi-PDF-Komponentenseite