knitr::opts_chunk$set( collapse = TRUE, comment = " " )
library(minisvg) library(purrr)
Editing SVG can be tricky because they are nested structures.
{minisvg}
contains some tools for interogating/manipulating existing SVG, but
the current focus in {minisvg} is really creating SVG documents in R.
A {minisvg} SVG document is just a tree of R6 objects, where each object represents
a single SVG tag (e.g. <rect>
or <pattern>
).
Each object has a list of children ($childen
) and
a list of attributes ($attrib
).
There is also an alternate representation of the child elements of a node in the
member $child
. E.g. All the children of the current element which are <rect>
tags
are stored in the list $child$rect
svg_text <- ' <svg width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <g> <circle fill="lightgrey" cx="50%" cy="50%" r = "45%" id="bgcircle" stroke="black" stroke-width="4" /> </g> <rect fill="red" x="40%" y="10%" width="20%" height="20%" /> <rect fill="yellow" x="40%" y="40%" width="20%" height="20%" /> <g> <rect fill="green" x="40%" y="70%" width="20%" height="20%" id="gogogo" stroke="black" stroke-width="4" /> </g> </svg>' doc_orig <- minisvg::parse_svg_doc(svg_text)
Show SVG text (click to open)
print(doc_orig)
if (interactive()) { doc_orig$show() } else { doc_orig }
$children
If the structure of an SVG document is known, the element may be selected by its
index in the $children
list of all child nodes.
In this example, the second child of the root document is selected (i.e. the red square) and its fill attribute is updated.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Extract the first rectangle and change its fill attribute #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ doc <- doc_orig$copy() first_child <- doc$children[[2]] first_child$attribs$fill <- 'pink'
Show SVG text (click to open)
print(doc)
if (interactive()) { doc$show() } else { doc }
Each SVG element node keeps both a $children
list of nodes (which is arranged by
insertion order), and a $child
list which is grouped by tag names.
All direct child elements with a particular tag name, e.g. circle
, may be accessed using
$child[['circle']]
.
In the following example, the two direct child rectangles have their fill
attribute updated.
Note that the small green rectangle is not updated as it is not a direct child of the root document.
doc <- doc_orig$copy() rects <- doc$child$rect purrr::walk(rects, ~.x$update(fill = 'blue'))
Show SVG text (click to open)
print(doc)
if (interactive()) { doc$show() } else { doc }
In the following example, the green rectangle is updated. It is accessed as the first child rectangle
of the second child g
tag.
doc <- doc_orig$copy() small_rect <- doc$child$g[[2]]$child$rect[[1]] small_rect$update(fill = 'lightblue')
Show SVG text (click to open)
print(doc)
if (interactive()) { doc$show() } else { doc }
In the following example, all elements with with green or light grey fill are found and updated
doc <- doc_orig$copy() rg_elems <- doc$find(attribs = list(fill=c('lightgrey', 'green'))) purrr::walk(rg_elems, ~.x$update(fill = 'blue'))
Show SVG text (click to open)
print(doc)
if (interactive()) { doc$show() } else { doc }
In the following example, all rectangles with a black outline (stroke) are found and updated
doc <- doc_orig$copy() rect_elems <- doc$find(tag = 'rect', attribs = list(stroke = 'black')) purrr::walk(rect_elems, ~.x$update(fill = 'black', stroke = 'green'))
Show SVG text (click to open)
print(doc)
if (interactive()) { doc$show() } else { doc }
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.