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)
<?xml version="1.0" encoding="UTF-8"?>
<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>
$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)
<?xml version="1.0" encoding="UTF-8"?>
<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="pink" 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>
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.
Show SVG text (click to open)
<?xml version="1.0" encoding="UTF-8"?>
<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="blue" x="40%" y="10%" width="20%" height="20%" />
<rect fill="blue" 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>
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)
<?xml version="1.0" encoding="UTF-8"?>
<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="lightblue" x="40%" y="70%" width="20%" height="20%" id="gogogo" stroke="black" stroke-width="4" />
</g>
</svg>
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)
<?xml version="1.0" encoding="UTF-8"?>
<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="blue" 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="blue" x="40%" y="70%" width="20%" height="20%" id="gogogo" stroke="black" stroke-width="4" />
</g>
</svg>
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)
<?xml version="1.0" encoding="UTF-8"?>
<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="black" x="40%" y="70%" width="20%" height="20%" id="gogogo" stroke="green" stroke-width="4" />
</g>
</svg>