/
editing-svgs.Rmd
245 lines (170 loc) · 5.04 KB
/
editing-svgs.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
---
title: "editing-svgs"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{editing-svgs}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = " "
)
```
```{r setup, message=FALSE}
library(minisvg)
library(purrr)
```
# Manipulating/Editing an Existing SVG
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.
# Overview of the structure of a {minisvg} document
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`
# Example SVG for this vignette
```{r}
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)
```
<details closed>
<summary> Show SVG text (click to open) </summary>
```{r echo=FALSE}
print(doc_orig)
```
</details> <br />
```{r echo = FALSE}
if (interactive()) {
doc_orig$show()
} else {
doc_orig
}
```
# Selecting and editing an element by its index in `$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.
```{r}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Extract the first rectangle and change its fill attribute
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
doc <- doc_orig$copy()
first_child <- doc$children[[2]]
first_child$attribs$fill <- 'pink'
```
<details closed>
<summary> Show SVG text (click to open) </summary>
```{r echo=FALSE}
print(doc)
```
</details> <br />
```{r echo = FALSE}
if (interactive()) {
doc$show()
} else {
doc
}
```
# Selecting and editing all direct child elements by type
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.
```{r}
doc <- doc_orig$copy()
rects <- doc$child$rect
purrr::walk(rects, ~.x$update(fill = 'blue'))
```
<details closed>
<summary> Show SVG text (click to open) </summary>
```{r echo=FALSE}
print(doc)
```
</details> <br />
```{r echo = FALSE}
if (interactive()) {
doc$show()
} else {
doc
}
```
# Selecting and editing a nested child element by type
In the following example, the green rectangle is updated. It is accessed as the first child rectangle
of the second child `g` tag.
```{r}
doc <- doc_orig$copy()
small_rect <- doc$child$g[[2]]$child$rect[[1]]
small_rect$update(fill = 'lightblue')
```
<details closed>
<summary> Show SVG text (click to open) </summary>
```{r echo=FALSE}
print(doc)
```
</details> <br />
```{r echo = FALSE}
if (interactive()) {
doc$show()
} else {
doc
}
```
# Finding any nested child element by attribute
In the following example, all elements with with green or light grey fill are found and updated
```{r}
doc <- doc_orig$copy()
rg_elems <- doc$find(attribs = list(fill=c('lightgrey', 'green')))
purrr::walk(rg_elems, ~.x$update(fill = 'blue'))
```
<details closed>
<summary> Show SVG text (click to open) </summary>
```{r echo=FALSE}
print(doc)
```
</details> <br />
```{r echo = FALSE}
if (interactive()) {
doc$show()
} else {
doc
}
```
# Finding any nested child element by attribute and tag name.
In the following example, all rectangles with a black outline (stroke) are found and updated
```{r}
doc <- doc_orig$copy()
rect_elems <- doc$find(tag = 'rect', attribs = list(stroke = 'black'))
purrr::walk(rect_elems, ~.x$update(fill = 'black', stroke = 'green'))
```
<details closed>
<summary> Show SVG text (click to open) </summary>
```{r echo=FALSE}
print(doc)
```
</details> <br />
```{r echo = FALSE}
if (interactive()) {
doc$show()
} else {
doc
}
```