Load SVG files as R vector objects (grobs) or data.frames

svgparser loads vector-based SVG images as R vector objects.

The vector objects which are created in R are the standard graphics object (‘grob’) in the base R {grid} graphics system.

SVG images can also be imported as data.frames. Each shape, line and point is represented in the data.frame with copious helpings of metadata to relate individual coordinates to the original SVG structure.

Example: Load SVG as vector image

This example loads an SVG image into a base R grid graphics object, and then draws the object using grid.draw.

Note that this is not a fixed size raster image! This is a vector image which can be resized without loss of precision.

tiger_filename <- system.file("tiger.svg", package = "svgparser")
tiger_grob <- svgparser::read_svg(tiger_filename)
grid::grid.draw(tiger_grob)

Example: Load SVG as a data.frame

This example loads an SVG as a data.frame and then plots the coordinates using ggplot. The element type from the original SVG is used as the aesthetic for colouring the lines in the plot.

tiger_df <- svgparser::read_svg(tiger_filename, obj_type = 'data.frame')

nrow(tiger_df)
#> [1] 57117
knitr::kable(head(tiger_df, 3))
x y name idx path_idx inst_idx path col fill alpha lwd lineend linejoin linemitre fontsize cex fontface fontfamily font xpath tag elem_idx
-122.3000 84.2850 move 1 1 1 m -122.3 84.285 #000000FF #FFFFFFFF 1 0.172 butt mitre 4 12 1 plain serif 1 /svg/g/g[1]/path path 1
-122.3000 84.2850 bezier3_reflect 1 2 2 s 0.1 1.894 -0.73 1.875 #000000FF #FFFFFFFF 1 0.172 butt mitre 4 12 1 plain serif 1 /svg/g/g[1]/path path 1
-122.2997 84.2916 bezier3_reflect 1 2 2 s 0.1 1.894 -0.73 1.875 #000000FF #FFFFFFFF 1 0.172 butt mitre 4 12 1 plain serif 1 /svg/g/g[1]/path path 1

There’s enough information in the data.frame to manually recreate most of the SVG using whatever drawing mechanism you’d like. Here, I’ve used ggplot2 to draw the outlines from the data.

ggplot(tiger_df) + 
  geom_path(aes(x, y, colour = name, group = interaction(elem_idx, path_idx))) +
  scale_y_reverse()

What’s in the box

Installation

This package is available on GitHub.

# install.package('remotes')
remotes::install_github('coolbutuseless/cssparser') # Handles CSS styling
remotes::install_github('coolbutuseless/svgparser')

With R > v4.1.0:

  • gradients and clipping paths are rendered as correctly as possible.

With R < v4.1.0:

  • solid colours will be used in place of gradients, and
  • any elements which use a clipping path in the SVG will not be properly clipped in R.

SVG Feature Support

SVG Feature Support
Feature Support Notes
<path> elements yes Working well!
<rect> <circle> etc yes Working
<ellipse> yes No rotation yet
<text> meh Works at a basic level. Don’t expect anything using fancy text effects to work
style cascading yes Using cssparser
<linearGradient> <radialGradient> yes converted to R4.1 gradient objects. ‘userSpaceOnUse’ coordinate space only. Send examples!
transform presentation attribute yes .
<defs> <use> yes .
<pattern> no Impossible?
<filter> no Impossible?
animation no Impossible?
linetypes no Planned feature - but R is much more limited compared to SVGs linetype features.
masks no need example SVG
clipping paths yes works on the few examples tested
<a> yes treated as a <g> grouping element. {grid} doesn’t support clickable links though.
<switch> meh supported; but instead of selecting the best child element it selects the first non-null child. Needs fixing.
<image> yes image support via {magick}. Does not currently handle transforms for images. Aspect ratio is fixed to match that of image.

SVG icon pack SuperTinyIcons is included with the package

SuperTinyIcons is a set of icons focussed on small representations of logos of some internet companies and other images.

These icons can be accessed from this package using:

  • supertinyicons (for the names of all the icons), and
  • load_supertinyicon() to load an icon as an R object.

See LICENSE-supertinycons.txt for MIT license information for this icon set.

supertinyicon_names[1:10]
load_supertinyicon('twitter', obj_type = 'svg')
 [1] "acast"          "access"         "adobe"          "airbnb"        
 [5] "amazon_alexa"   "amazon_s3"      "amazon"         "amberframework"
 [9] "andotp"         "android"       

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" aria-label="Twitter" role="img" viewBox="0 0 512 512">
  <rect width="512" height="512" rx="15%" fill="#1da1f2"/>
  <path fill="#fff" d="M437 152a72 72 0 01-40 12a72 72 0 0032-40a72 72 0 01-45 17a72 72 0 00-122 65a200 200 0 01-145-74a72 72 0 0022 94a72 72 0 01-32-7a72 72 0 0056 69a72 72 0 01-32 1a72 72 0 0067 50a200 200 0 01-105 29a200 200 0 00309-179a200 200 0 0035-37"/>
</svg>
grid.draw(load_supertinyicon('twitter'))

Technical bits

svgparser is vanilla R code which walks the XML tree and converts elements (and nested sub-elements) into a nested set of grid::grobTree() objects.

Circles, lines and points are translated to their corresponding grid object i.e. grid::circleGrob(), grid::linesGrob(), grid::pointsGrob().

Radial and linear gradients are captured as the new objects available in R4.1: grid::radialGradient() and grid::linearGradient()

For <use> tags, svgparser extracts xpath information to retrieve relevant referenced section of the SVG, parse it into R objects and then insert it at the given location.

There are only a few dependencies:

  • {xml2} is used as to parse the SVG into R
  • {cssparser} is used to cascade stylesheets within the XML representation. This was initially built-in to svgparser, but was moved to a separate package as things got complicated. (Who could have known that parsing and cascading style sheets could be so complicated!)
  • {stringi} is used as part of the lexer which breaks some of the SVG structures into smaller objects for parsing e.g. transform strings like translate(10, 10) rotate(50)
  • {rsvg} renders SVG files into bitmaps, and some other file representations.
    • This package is wraps the C library librsvg
  • {grImport2} reads SVG files into R
    • grImport2 only seems to load SVG created by the Cairo graphics library
svgparser rsvg grImport2
Loads as Vector Object Yes (grob) No (bitmap) Yes (grob)
Loads as Standard Grob Yes (grobTree) No (bitmap) No (custom PictureGrob)
Exposes coordinates Yes (as a data.frame) along with SVG meta-information No Yes (via grid::grobPoints), but lacks meta-information
Note Backed by fast + well tested C library Will only load SVG created by Cairo

Acknowledgements