Reputation: 38003
I'm trying to import the coordinates of a path in an SVG file created with illustrator into R.
I thought I might read the SVG into R with grImport2
, which should in theory be importing SVG files, but I think they might only handle SVG files generated by the Cairo device.
Let's say I want to import the following .svg file:
Here is my attempt at loading an SVG file. If I read the content correctly, it should just contain 1 (complicated) path. The warning is the same one I get when loading my SVG file created by Adobe Illustrator. Be warned that the code below will get stuck for some time!
file <- "https://upload.wikimedia.org/wikipedia/commons/d/db/Brain_Drawing.svg"
download.file(file, tmp <- tempfile(fileext = ".svg"))
# Don't run the following line, it will get your R session stuck!
x <- grImport2::readPicture(tmp)
#> Warning message:
#> In checkValidSVG(doc, warn = warn) :
#> This picture was not generated by Cairo graphics; errors may result
unlink(tmp)
My ideal output would be a data.frame
with at least x
and y
coordinates of (anchor)points and perhaps some metadata that can tell different paths apart. I don't need curves and arcs interpolated or anything like that.
Are there any other packages I'm not aware of that might import this? Is there a way to convert the SVG to one that I can read into R?
Upvotes: 3
Views: 1094
Reputation: 174278
Nice question Teunbrand - thanks for posting. The issue does seem to be getting grImport2
to read non-Cairo svg. So actually, we just need another translation step: use package rsvg
to read in a non-Cairo svg and and write the same thing with Cairo. Oddly enough, it has a function to do this, called rsvg_svg
. So we can read the remote file as a Picture
object in a single line without even creating a local tmp file:
file <- "https://upload.wikimedia.org/wikipedia/commons/d/db/Brain_Drawing.svg"
svg <- grImport2::readPicture(rawToChar(rsvg::rsvg_svg(file)))
Unfortunately, a Picture
is a deeply listed S4 object which is harder to navigate than a grob tree. I'm sure a person of your caliber could coerce it into a data frame, but it's not trivial, and I won't attempt it here. At least the components themselves look easy enough to harvest:
svg@content[[1]]@content[[1]]@d@segments[[3]]
#> An object of class "PathCurveTo"
#> Slot "x":
#> [1] 791.8359 789.5286 787.1382 784.6293 782.0041 779.2962 776.5600 773.8555 771.2318
#> [10] 768.712 766.2875 764.8359
#>
#> Slot "y":
#> [1] 8.191406 8.209760 8.239691 8.282891 8.340521 8.412835 8.499026 8.597442 8.706126
#> [10] 8.82357 8.949580 9.031250
Anyway, there are a few nice utility functions that allow you to do cool stuff like this:
brainGrob <- grImport2::pictureGrob(svg)
ggplot2::ggplot() + ggplot2::geom_point(ggplot2::aes(1, 1))
grid::grid.draw(brainGrob)
I haven't seen an arbitrary SVG drawn in grid before, so I was pleased to find this, prompted by your question. Thanks again.
Upvotes: 4