Technical Article

Import SVG into PDF as Reusable Form XObjects

HotPDF imports an SVG file into a loaded or authored PDF as a reusable Form XObject through ImportSVGFormXObject, which returns the resource name of the created form. Placing it is a separate call, DrawSVGFormXObject, so one import can be stamped on many pages at different sizes without duplicating a single byte of the artwork

That split matters more than it sounds. A logo rasterized at 300 DPI and dropped on 400 pages is 400 copies of a bitmap. The same logo as a Form XObject is one object referenced 400 times, it stays sharp at any zoom, and it prints at device resolution rather than at whatever DPI seemed sufficient at build time

HotPDF diagram of one SVG import creating a single PDF Form XObject that DrawSVGFormXObject places on many pages at different sizes, compared with hundreds of raster bitmap copies
One import writes the artwork once as a Form XObject with its own resources, and each placement is a small invocation that adds no duplicate bytes

Why a Form XObject instead of drawing the SVG each time?

A Form XObject is PDF's own mechanism for reusable content: a self-contained content stream with its own bounding box and resource dictionary, invoked by name. Importing SVG into one gives you three properties that direct drawing does not. The artwork exists once in the file. Each placement is a small matrix and an invocation. And the shapes stay vectors, so a viewer at 800 percent zoom draws curves, not pixels

The subtlety is resources. A form's fonts and graphics states must live in the form's own /Resources dictionary, not in the resources of whichever page happened to be current at import time. HotPDF writes the Base 14 fonts and the ExtGState entries a form needs into the form itself, because a form that borrows a name from its first host page renders correctly there and produces an unresolved resource name on every other page it is later placed on

Two flips, one for geometry and one for glyphs

SVG's coordinate system grows downward from the top-left corner; a PDF Form XObject uses PDF user space, which grows upward from the bottom-left. The root viewBox transform performs one Y-axis flip so the geometry lands correctly. Text then needs a second, local flip, because a glyph drawn in an already-flipped frame would be upside down

HotPDF diagram of the two Y-axis flips between SVG and PDF coordinates: the root viewBox transform corrects the geometry and text glyphs need a second local flip to stay upright
The root viewBox transform flips the Y axis once so geometry lands in PDF space, and text takes a second, local flip or every glyph arrives upside down

This is the same correction that appears in reverse when a PDF page is written out as SVG, described in exporting PDF pages to SVG. Getting it wrong is easy to spot in artwork with text and almost invisible in artwork without, which is why a logo with a tagline is the better smoke test than a logo alone

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');

    // One import, many placements: the artwork bytes exist once
    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;

How do hyperlinks survive the trip?

They cannot survive inside the form, because a Form XObject is content and PDF link targets are annotations, which belong to a page. An <a> element in the source SVG therefore cannot simply be copied into the form's content stream

HotPDF resolves this by keeping link rectangles as form-space metadata during parsing, and materializing them as page-level link annotations at each placement. DrawSVGFormXObject maps those rectangles through the target rectangle you pass, so a form placed twice at different sizes gets two correctly sized sets of hit areas. Link rectangles are mapped through the root size only and never enter the content stream, which keeps the drawn artwork identical regardless of how many links it carries. Pass AddLinks = False when you want the artwork without the interactivity, for example on a print-only page

What the importer refuses, and why that is the point

SVG is an XML dialect with scripting, external references and entity expansion, which makes an arbitrary SVG file a hostile input in the same way an arbitrary HTML file is. The import profile is fail-closed on everything that reaches outside the document: external resource references, scripts, event handler attributes, DTDs and any url() that is not a local fragment reference

Fail-closed means the import fails rather than silently dropping the offending construct. That distinction matters for a batch pipeline, because a silent drop produces a document that is missing something nobody knows about, while a refusal produces a file you can quarantine and inspect. If you accept SVG uploads from users, this is the behavior you want, and it pairs with the process-level isolation described in codec process isolation for untrusted images

HotPDF diagram of a fail-closed SVG import profile that accepts local vector features and refuses scripts, external references and DTDs by failing the import instead of dropping them silently
The import profile refuses scripts, external references and DTDs by failing outright, which gives you a file to quarantine instead of a document that is quietly incomplete

One internal boundary is worth knowing if you also render color fonts. The strict SVG subset used for OpenType SVG color glyphs runs through a separate entry point and keeps its original, narrower failure behavior. General artwork import is a distinct path, so adding arc, transparency and CSS support to imported artwork did not widen what a font glyph is allowed to contain

Gradients, masks and the no-raster rule

Gradients import as bounded vector color bands constrained by the original geometry's clipping path, not as a rasterized image. Masks use the same approach: the mask outline becomes a vector clipping path. The result is that a gradient-heavy illustration stays resolution independent and stays small, where a rasterizing importer would produce a large image object whose quality is fixed at import time

The trade is fidelity in the extreme cases. A banded approximation of a smooth gradient is not mathematically identical to the SVG gradient, and artwork with dozens of overlapping soft masks will look closer to the original if you render it to a high-resolution image instead. For the shading models PDF supports natively, and when to reach for them directly, see rendering axial and radial shadings

// Reading the import statistics tells you what actually came through
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]));

// Placing the same form at two sizes produces two correct link sets
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);

// Print-only placement: artwork without the hit areas
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);

Where SVG import fits next to the other vector routes

Three vector import routes exist and they are not interchangeable. SVG import is right for artwork produced by design tools and for anything that arrives from a web pipeline. Metafile import, covered in EMF and WMF vector import, is right for content generated by Windows applications and by charting components that draw to a device context. Direct drawing through the page API is right for artwork your own code computes, where there is no intermediate format at all

The reusability argument favors SVG import for repeated elements specifically. Letterheads, logos, watermarks, certification marks and decorative rules are all drawn identically on many pages, and each of them is one form object plus one invocation per page. Measure the file size difference on a real document before deciding it does not matter: on a 500-page catalog with a vector logo per page, the gap between one form and 500 inline copies is usually several megabytes

SVG import, metafile import and direct vector drawing share the same content stream writer for Delphi and C++Builder; the complete feature list is on the HotPDF Delphi PDF component page