On this page:
svg-string->bitmap
svg-file->bitmap
svg-string->pict
svg-file->pict
svg-doc->pict
render-svg-doc!
read-svg-document
svg-doc-width
svg-doc-height
svg-doc-id-table
svg-doc-view-matrix

3 Top-Level Rendering🔗ℹ

This is the small set of functions most programs need: parse an SVG document and get back something you can display or save. Everything else in this document is either a building block these functions use internally (and export for reuse) or a lower-level entry point for working with a piece of the pipeline in isolation.

The two output formats — bitmap% and pict are not just two ways of returning the same picture:

  • bitmap% (svg-string->bitmap, svg-file->bitmap) rasterizes onto a fresh bitmap sized exactly to the SVG’s own width/height, with an opaque white background the right default for "save this as a standalone image."

  • pict (svg-string->pict, svg-file->pict, svg-doc->pict) wraps the same rendering as a self-drawing pict with a transparent background, so it composes correctly with other picts around or behind it. Because render-svg-doc! never assumes bitmap-dc% for the dc<%> it’s handed (only for its own private offscreen buffers — mask, filter, pattern, and marker content, which is unrelated), the resulting pict can be drawn onto any dc<%>: a bitmap via pict->bitmap, but also a pdf-dc% or post-script-dc%, giving vector output for anything that doesn’t need mask/filter rasterization. (Masked or filtered elements still rasterize internally either way, the same as in any renderer, since those effects inherently require pixel buffers — they’d appear as embedded raster images in an otherwise-vector PDF.)

Both families accept either a string of SVG source or a file path; the file-path versions additionally know the SVG’s own directory, which matters for resolving relative <image> references — an <image href="icon.png"> next to diagram.svg only resolves correctly if you render via (svg-file->bitmap "diagram.svg"), not by reading the file yourself and passing its contents to svg-string->bitmap.

If you need to inspect or reuse a parsed document before rendering it (its declared width/height, its id table, or its viewBox matrix), read-svg-document and the svg-doc-* accessors below let you separate parsing from rendering.

procedure

(svg-string->bitmap s)  (is-a?/c bitmap%)

  s : string?
Parses s as an SVG document and renders it to a freshly-created bitmap%, sized to the document’s own width and height (each rounded up to the nearest whole pixel, with a minimum of 1). The bitmap has an opaque white background.

(send (svg-string->bitmap
       #<<SVG
<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" fill="crimson"/>
</svg>
SVG
       )
      save-file "circle.png" 'png)

a solid crimson circle on a white background

procedure

(svg-file->bitmap path)  (is-a?/c bitmap%)

  path : path-string?
Like svg-string->bitmap, but reads the document from path. Any relative <image> href inside the document resolves against path’s own directory (see current-svg-base-dir), so prefer this over reading the file yourself and calling svg-string->bitmap whenever the document might reference local images by relative path.

(svg-file->bitmap "logo.svg")

procedure

(svg-string->pict s)  pict?

  s : string?
Parses s as an SVG document and returns a pict that draws it, sized to the document’s own width and height. The pict’s background is transparent (unlike svg-string->bitmap’s opaque white), so it composes correctly with other picts.

(require pict)
(hc-append 10 (svg-string->pict "...") (colorize (disk 20) "orange"))

Constructing the pict costs a second, throwaway render as part of pict’s own contract checking (dc’s precondition renders once with a scratch dc<%> to confirm the draw procedure restores its state correctly) — this is pict’s behavior, not something svg/svg adds on top, and it’s a one-time cost per pict, not per subsequent draw.

procedure

(svg-file->pict path)  pict?

  path : path-string?
Like svg-string->pict, but reads the document from path, with relative <image> references resolved against path’s own directory — the pict analogue of svg-file->bitmap, for the same reason (see current-svg-base-dir).

(svg-file->pict "logo.svg")

procedure

(svg-doc->pict doc [base-dir])  pict?

  doc : svg-doc?
  base-dir : (or/c path? #f) = #f
Wraps an already-parsed svg-doc (from read-svg-document) as a pict, without re-parsing. svg-string->pict and svg-file->pict are both thin wrappers around this. base-dir, if given, is used the same way current-svg-base-dir is elsewhere: as the directory relative <image> references in doc resolve against. Passing it here (rather than wrapping the call in (parameterize ([current-svg-base-dir ...]) ...) yourself) matters because pict’s dc constructor draws lazily — the draw procedure can run well after this call returns, outside any parameterize’s dynamic extent, so svg-doc->pict re-establishes the parameterization itself, every time the pict is actually drawn.

Use this when you’re calling read-svg-document yourself anyway — for instance, to inspect svg-doc-width/svg-doc-height before deciding how to render — and want to avoid parsing the document twice.

procedure

(render-svg-doc! doc dc)  void?

  doc : svg-doc?
  dc : (is-a?/c dc<%>)
Renders an already-parsed svg-doc directly onto dc, applying the document’s own viewBox transform and drawing every element in turn. This is the function every other rendering entry point in this library eventually calls; use it directly when you already have a dc<%> you want to draw onto — an existing bitmap, a canvas, a PDF page — rather than getting a fresh bitmap or pict back.

Unlike svg-doc->pict, this does not save or restore the dc’s transformation, smoothing mode, pen, brush, font, or other drawing state around the call — it applies the viewBox transform and leaves it applied, and generally assumes it can freely change dc’s state. If you need the dc’s prior state preserved (for instance, because you’re drawing several things onto the same dc in sequence), save and restore it yourself around the call, the way svg-doc->pict does internally.

(define bm (make-object bitmap% 200 200))
(define dc (new bitmap-dc% [bitmap bm]))
(render-svg-doc! (read-svg-document "<svg>...</svg>") dc)

procedure

(read-svg-document x)  svg-doc?

  x : (or/c string? input-port?)
Parses x (SVG source, as a string or an input port) into an svg-doc: the document’s root element, its declared width and height, a table mapping every id attribute in the document to its element (used to resolve url(#id)/href="#id" references), its viewBox transform matrix, and its parsed <style> stylesheet (if any). This is the parsing step that every top-level rendering function performs before rendering; call it directly when you want to inspect a document (its dimensions, in particular) before deciding how or whether to render it, or when you want to render the same parsed document more than once without re-parsing.

Width and height resolve in the following order: the width/height attributes if present and not given as a percentage; otherwise the viewBox attribute’s width/height, if present; otherwise 300×150 (the CSS replaced-element default).

(define doc (read-svg-document #<<SVG
<svg width="640" height="480">...</svg>
SVG
                                ))
(svg-doc-width doc)   ; 640
(svg-doc-height doc)  ; 480

procedure

(svg-doc-width doc)  real?

  doc : svg-doc?

procedure

(svg-doc-height doc)  real?

  doc : svg-doc?
The document’s own width and height, in user units (CSS pixels), resolved as described under read-svg-document.

procedure

(svg-doc-id-table doc)  (hash/c string? any/c)

  doc : svg-doc?
A hash mapping every id attribute value found anywhere in the document to the (xexpr-represented) element that carries it — the table url(#id)/href="#id" references are resolved against during rendering. Useful if you want to inspect or extract a specific element (a particular <symbol>, gradient, or filter definition) from an already-parsed document.

procedure

(svg-doc-view-matrix doc)

  (vector/c real? real? real? real? real? real?)
  doc : svg-doc?
The document’s root viewBox transform, as a #(a b c d e f) vector in the format dc<%>’s own transform method accepts (x '= a·x + c·y + e, y '= b·x + d·y + f) — the matrix render-svg-doc! applies once, at the very start, before drawing anything. If the document has no viewBox, this is the identity matrix #(1 0 0 1 0 0).