\documentclass[article]{jss} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% declarations for jss.cls %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% almost as usual \author{Paul Murrell\\The Unversity of Auckland \And Simon Potter\\The Unversity of Auckland} \title{The \pkg{gridSVG} package for R} %% for pretty printing and a nice hypersummary also set: \Plainauthor{Paul Murrell, Simon Potter} %% comma-separated \Plaintitle{The gridSVG Package for R} \Shorttitle{The gridVSG Package} %% a short title (if necessary) %% an abstract and keywords \Abstract{ The abstract of the article. } \Keywords{world-wide web, graphics, \proglang{R}, \proglang{SVG}} \Plainkeywords{world-wide web, graphics, R, SVG} %% without formatting %% at least one keyword must be supplied %% publication information %% NOTE: Typically, this can be left commented and will be filled out by the technical editor %% \Volume{13} %% \Issue{9} %% \Month{September} %% \Year{2004} %% \Submitdate{2004-09-29} %% \Acceptdate{2004-09-29} %% The address of (at least) one author should be given %% in the following format: \Address{ Paul Murrell\\ Department of Statistics\\ The University of Auckland\\ 38 Princes Street, Auckland\\ New Zealand\\ E-mail: \email{paul@stat.auckland.ac.nz}\\ URL: \url{http://www.stat.auckland.ac.nz/~paul/} } %% It is also possible to add a telephone and fax number %% before the e-mail in the following format: %% Telephone: +43/1/31336-5053 %% Fax: +43/1/31336-734 %% for those who use Sweave please include the following line (with % symbols): %% need no \usepackage{Sweave.sty} %% end of declarations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \usepackage{boxedminipage} \usepackage{listings} \newcommand{\svg}{\proglang{SVG}} \newcommand{\png}{\proglang{PNG}} \newcommand{\pdf}{\proglang{PDF}} \newcommand{\jpeg}{\proglang{JPEG}} \newcommand{\R}{\proglang{R}} \newcommand{\ggobi}{\proglang{GGobi}} \newcommand{\xml}{\proglang{XML}} \newcommand{\xpointer}{\proglang{XPointer}} \newcommand{\xpath}{\proglang{XPath}} \newcommand{\cairo}{\proglang{Cairo}} \newcommand{\js}{\proglang{JavaScript}} \newcommand{\json}{\proglang{JSON}} \newcommand{\mpg}{\proglang{MPEG4}} \newcommand{\java}{\proglang{Java}} \newcommand{\html}{\proglang{HTML}} \newcommand{\mathml}{\proglang{MathML}} \newcommand{\mac}{\proglang{MacOS X}} \newcommand{\linux}{\proglang{Linux}} \newcommand{\windows}{\proglang{Microsoft Windows}} \newcommand{\dfn}[1]{\emph{#1}} \begin{document} \SweaveOpts{keep.source = true, eps = false} <>= options(prompt="R> ", continue=" ", useFancyQuotes=FALSE) splitLines <- function(x, pattern, where, align, replace=where) { txt <- strsplit(x, "\n")[[1]] lines <- grep(pattern, txt) for (i in lines) { indent <- paste(rep(" ", regexpr(align, txt[i]) - 1), collapse="") txt[i] <- gsub(where, paste0("\n", indent, replace), txt[i]) } paste(txt, collapse="\n") } @ %% include your article here, just as usual %% Note that you should use the \pkg{}, \proglang{} and \code{} commands. \section{Introduction} %% Note: If there is markup in \(sub)section, then it has to be escape as above. The \pkg{gridSVG} package for \R{} \citep{RJournal:2014-1:murrell-potter} provides functions to export \R{} plots to an \svg{} format, which is useful for embedding \R{} plots in web pages. Ths \svg{} format is appropriate for use in web pages because it is a vector format, rather than a raster format like \png{} or \jpeg{}, so images scale nicely, because \svg{} is a modern graphics format that includes features such as fill patterns and filter effects, because \svg{} images can include dynamic features like animation, and because \svg{} is a markup language that integrates well with \html{}. The last point is important because it leads to the ability to add interactivity to a web page by using \js{} to manipulate of the content of the web page. It is possible to export an \R{} plot to an \svg{} format using the built-in \code{svg()} graphics device in \R{}, but plots exported that way have no dynamic features such as animation, are limited to the capabilities of the \R{} graphics engine, which excludes pattern fills and filter effects, and are not well set up for manipulation with \js{}. The \pkg{gridSVG} package adds the ability to enhance \R{} plots with some dynamic features such as hyperlinks and animation, provides access to the more advanced \svg{} features such as pattern fills, and produces structured and labelled \svg{} code that is designed to facilitate manipulation with \js{}. The main limitation of the \pkg{gridSVG} package is that it can only export \R{} plots that are based on the \pkg{grid} graphics package. This means that plots from, for example, \pkg{lattice} and \pkg{ggplot2} can be exported with \pkg{gridSVG}, but the large number of plots from the \pkg{graphics} package and packages that build on it cannot be directly exported. However, a workaround is provided by the \pkg{gridGraphics} package \citep{gridGraphics}, which has functions to convert \pkg{graphics} plots to \pkg{grid} equivalents that can be exported. Several other packages provide alternative ways to export \R{} plots in an \svg{} format. The \pkg{Cairo} package provides a \code{CairoSVG()} function as an alternative to the built-in \code{svg()} function, which can be useful if \R{} has not been installed with \cairo{} support. There is also a \code{devSVG()} function in the \pkg{RSvgDevice} package, which has somewhat lower system requirements so may be easier to install. Neither of those packages provides access to additional \svg{} features. The \pkg{RSVGTipsDevice} package builds upon \pkg{RSvgDevice} and adds a small set of extra features (tooltips and hyperlinks). The \pkg{SVGAnnotation} package goes much further, with support for adding interaction to \R{} plots in an \svg{} format. More details about the difference between \pkg{gridSVG} and \pkg{SVGAnnotation} will be revealed as we describe more about how \pkg{gridSVG} works, but the main advantage of \pkg{SVGAnnotation} (and all of the other packages mentioned here) is that they will export \emph{all} \R{} plots, whereas \pkg{gridSVG} can only export plots based on \pkg{grid}. The advantage of \pkg{gridSVG} is that it has a simpler design and, consequently is more robust and easier to extend than \pkg{SVGAnnotation}. Some other approaches to creating \R{} plots for web pages, particularly through \js{} libraries, as supported by the \pkg{rCharts}, \pkg{googleVis}, and \pkg{ggvis} packages, will also become relevant as we discuss the design of \pkg{gridSVG} in later sections. There are several possible reasons for using the \pkg{gridSVG} package: \begin{description} \item[Export an \R{} plot to an \svg{} format:] Although this can only be done for \pkg{grid} plots and although there are other functions and packages that can perform this task, there are still some benefits to using \pkg{gridSVG} to perform this task because the \svg{} code that is produced by \pkg{gridSVG} has some desirable features (see Section \ref{section:export}). \item[Enhance an \R{} plot with \svg{} features:] The \R{} graphics engine does not support a number of modern graphics features that are possible in the \svg{} format, such as hyperlinks, filters, and animations. The \pkg{gridSVG} package allows these \svg{} features to be incorporated into an \R{} plot (or at least into a \pkg{grid} plot). \item[Generate \svg{} content from \R{}:] While \svg{} provides sophisticated graphics features, it is not a simple matter to write raw \svg{} code for a complex image. The \pkg{gridSVG} package can also be viewed as a high-level tool for generating \svg{} content (not just plots). \end{description} This article begins with a basic overview of \pkg{gridSVG} usage, but its main focus is to document the full range of \pkg{gridSVG} features, to describe the implementation of \pkg{gridSVG} in sufficient detail to allow users and developers to access all of the features of the package, and to evaluate the design of the \pkg{gridSVG} package. Section \ref{sec:export} describes in detail how the \pkg{gridSVG} package exports a \pkg{grid} plot to \svg{}. This will allow us to see that there are benefits from exporting even a static, unembellished plot to \svg{} using \pkg{gridSVG}. Section \ref{sec:enhance} describes how \pkg{gridSVG} can be used to enhance a \pkg{grid} plot with many \svg{} features that are not normally possible in \R{} plots. This will demonstrate that \pkg{gridSVG} can be used to create dynamic and interactive versions of \R{} plots. It will also demonstrate that \pkg{gridSVG} provides a powerful platform for generating \svg{} content. Section \ref{sec:manipulate} describes how the \svg{} content that has been produced by \pkg{gridSVG} can be manipulated using \js{} or other tools. This will demonstrate further the benefits of generating \svg{} content using \pkg{gridSVG}. The article closes with a detailed discussion of the strengths and weaknesses of the approach taken by \pkg{gridSVG}, especially compared to the approaches taken by other software tools. \section{Basic usage} The simplest usage of \pkg{gridSVG} involves exporting an \R{} plot to an \svg{} format. The \code{gridsvg()} function is used to start a \pkg{gridSVG} graphics device and any subsequent \pkg{grid} graphics output is captured by that device until the \code{dev.off()} function is called to close the device. The first argument to \code{gridsvg()} is the name of the \svg{} file that will be created. The following code shows an example that exports a \pkg{lattice} plot to an \svg{} file called \code{lattice.svg} (see Figure \ref{fig:lattice}). <<>>= library(lattice) @ <<>>= library(gridSVG) @ <>= print(dotplot(variety ~ yield | site, data = barley, groups = year, subset = as.numeric(site) < 4, layout = c(1, 3))) @ <<>>= gridsvg("lattice.svg") <> dev.off() @ <>= temp <- readLines("lattice.svg") writeLines(gsub('encoding="ASCII"', "", temp), "lattice.svg") # 133.35 comes from 504px / 96dpi (assumed screen res) * 25.4 (mm conversion) # NOTE that 504 comes from 7in * 72dpi (PDF device resolution) system("wkhtmltopdf --page-width 133.35 --page-height 133.35 lattice.svg lattice.pdf") @ \begin{figure} \begin{center} \includegraphics[width=.8\textwidth]{lattice} \end{center} \caption{\label{fig:lattice}A \pkg{lattice} plot exported to \svg{} using the \code{gridsvg()} function from \pkg{gridSVG}.} \end{figure} An alternative workflow involves generating graphics on any standard \R{} graphics device, e.g., a graphics window on screen, and then calling the \code{grid.export()} function to export the graphics to an \svg{} file. \subsection[Testing for gridSVG compatibility]{Testing for \pkg{gridSVG} compatibility} If we are unsure whether a plot can be exported with \pkg{gridSVG}, we can use the \code{grid.ls()} function from the \pkg{grid} package to perform a quick test. The \code{grid.ls()} function prints a list of everything that the \pkg{grid} package has drawn on the current \R{} graphics device. The following code uses the \pkg{lattice} package to draw the dot plot from Figure \ref{fig:lattice}. This plot is drawn with \pkg{grid}, so the \code{grid.ls()} function prints out the names of all of the objects in the plot that \pkg{grid} has drawn. <<>>= library(grid) @ <<>>= <> grid.ls() @ The following code draws a similar dotplot (see Figure \ref{fig:dotchart}), but this time using the \code{dotchart()} function from the \pkg{graphics} package. For this plot, the \code{grid.ls()} function prints no output (because the plot was not drawn using \pkg{grid}), which indicates that the \pkg{gridSVG} package is not able to export this plot. <>= grid.newpage() @ <>= someBarley <- barley[as.numeric(barley$site) < 4, ] someBarley1931 <- someBarley[someBarley$year == 1931, ] barleyMatrix1931 <- with(someBarley1931, do.call("rbind", split(yield, variety))) colnames(barleyMatrix1931) <- someBarley1931$site[1:3] dotchart(barleyMatrix1931, xlim=range(someBarley$yield), cex=.8, bg="#ff00ff") par(new=TRUE) someBarley1932 <- someBarley[someBarley$year == 1932, ] barleyMatrix1932 <- with(someBarley1932, do.call("rbind", split(yield, variety))) colnames(barleyMatrix1932) <- someBarley1932$site[1:3] dotchart(barleyMatrix1932, xlim=range(someBarley$yield), cex=.8, bg="#0080ff") @ <>= # grid.ls() produces no output grid.ls() @ <>= <> grid.ls() @ \begin{figure} \begin{center} \includegraphics[width=.8\textwidth]{gridsvg-dotchart} \end{center} \caption{\label{fig:dotchart}A plot that was produced with the \code{dotchart()} function from the \pkg{graphics} package.} \end{figure} \subsection{Enhancing exported plots} The list of names of \pkg{grid} output on the current graphics device, as printed by \code{grid.ls()}, is the basis for all enhancements that we can make to an exported plot with \pkg{gridSVG}. Each function in \pkg{gridSVG} that adds an \svg{} feature to a plot requires the name of a piece of \pkg{grid} output to add the feature to. For example, the following code uses the \code{grid.hyperlink()} function to add a hyperlink to the text label in the bottom strip of the \pkg{lattice} plot from Figure \ref{fig:lattice}. The text object that we want to add the hyperlink to is identified by its name, \code{"plot\_01.textr.strip.1.1"}. The result, exported to an \svg{} file and viewed in a web browser, is shown in Figure \ref{fig:lattice-hyperlink}. <>= grid.hyperlink("plot_01.textr.strip.1.1", href="https://en.wikipedia.org/wiki/Grand_Rapids,_Minnesota") @ <>= gridsvg("lattice-hyperlink.svg", res=96) <> <> dev.off() @ % sleep 3; shutter -a -e -o lattice-hyperlink-svg.png \begin{figure} \begin{center} \includegraphics[width=.8\textwidth]{lattice-hyperlink-svg.png} \end{center} \caption{\label{fig:lattice-hyperlink}The \pkg{lattice} plot from Figure \ref{fig:lattice} with a hyperlink added to the text ``Grand Rapids'' in the bottom strip. The mouse is hovering over the text and the browser is showing the hyperlink target in the bottom-left of its window. If we click the text, we will navigate to the Wikipedia page for Grand Rapids, Minnesota.} \end{figure} This need for predictable names on \pkg{grid} objects places another limit on the usefulness of \pkg{gridSVG}. If we draw a plot with \pkg{grid} and the objects in that plot are not named, it is very difficult to specify which object we want to modify. On the plus side, most \pkg{grid} objects are given useful names in \pkg{lattice} and \pkg{ggplot2} plots. Many more examples of the enhancements that can be made to plots with \pkg{gridSVG} will be given in Section \ref{sec:enhance}. \subsection{A word on examples} The typical use of \pkg{gridSVG} involves these steps: draw a plot, determine the name of a piece of the plot that we want to enhance, call a \pkg{gridSVG} to modify that named piece of the plot, and export the plot to \svg{}. In order to keep code examples simple, and so that we can show full details, and because plots are not the only thing that we can draw with \pkg{grid}, many of the examples in the remaining sections of this article will not involve plots. Instead the steps will involve: draw a simple image, determine the name of a piece of the image that we want to enhance, call a \pkg{gridSVG} function to modify that named piece of the image, and export the image to \svg{}. Because \pkg{grid} makes no distinction between an image that is a plot and any other sort of image, everything that we demonstrate through simple examples should generalise easily to working with more complex images, such as plots. Because the \pkg{gridSVG} package can only export images drawn with the \pkg{grid} package, the examples will only involve \pkg{grid} functions and, because we are keeping the examples simple, most examples will involve raw \pkg{grid} function calls, rather than calls to functions from \pkg{lattice} or \pkg{ggplot2}. \subsection{Two types of functions} The \pkg{gridSVG} package, like the \pkg{grid} package, in many cases provides two versions of a function: one of the form \code{grid.foo()} and one of the form \code{fooGrob()}. The purpose of \code{grid.foo()} functions is to modify something that has already been drawn. The first argument is usually the name of a \pkg{grid} object on the current graphics device. The effect is to modify what has been drawn and no result is returned. This is useful when we have used someone else's code, for example when we have drawn a plot using \pkg{lattice} or \pkg{ggplot2}, and we want to modify that objects that their code has drawn. The purpose of the \code{fooGrob()} functions is to modify a \pkg{grid} object that has not yet been drawn. The result is the modified object. This \code{fooGrob()} functions allow an image to be composed entirely off-screen and then the image is only drawn once it is complete. This is useful when we are writing our own code to draw an image. \section[Exporting plots to SVG]{Exporting plots to \svg{}} \label{section:export} This section describes the strategy used by the \pkg{gridSVG} package to export a plot from \R{} to an \svg{} format. We begin with a discussion of how built-in \R{} graphics devices work to export \R{} plots to formats like \pdf{} and \svg{}. \subsection[R graphics devices]{\R{} graphics devices} Figure \ref{fig:graph} shows a diagram of the \R{} graphics packages that are relevant to this article. The package \pkg{grDevices} contains the built-in \R{} graphics devices that allow \R{} plots to be save in a \png{} or \pdf{} or \svg{} format (among others). All functions in both \pkg{graphics} and \pkg{grid}, and all packages that build upon them, work with all \R{} graphics devices, so any \R{} plot can be exported to any format that \R{} supports. <>= library(gridGraphviz) library(graph) gnel <- new("graphNEL", nodes=c("lattice", "ggplot2", "grid", "graphics", "grDevices", "gridSVG", "PNG", "PDF", "SVG"), edgeL=list(lattice=list(edges="grid"), ggplot2=list(edges="grid"), grid=list(edges=c("grDevices", "gridSVG")), graphics=list(edges="grDevices"), grDevices=list(edges=c("PNG", "PDF", "SVG")), gridSVG=list(edges="SVG"), PNG=list(), PDF=list(), SVG=list()), edgemode="directed") rag <- agopenTrue(gnel, "", nodeAttrs= list(shape=c(lattice="ellipse", ggplot2="ellipse", grid="ellipse", graphics="ellipse", grDevices="ellipse", gridSVG="ellipse", PNG="rect", PDF="rect", SVG="rect"), fillcolor=c(lattice="grey", ggplot2="grey", grid="grey", graphics="grey", grDevices="grey", gridSVG="black", PNG="white", PDF="white", SVG="white"))) for (i in seq(along = AgEdge(rag))) { AgEdge(rag)[[i]]@arrowhead <- "normal" } pdf("graph-%d.pdf", width=4, height=4, onefile=FALSE) # grid.newpage() grid.graph(rag) grid.edit("label-gridSVG", gp=gpar(col="white")) dev.off() @ \begin{figure} \begin{center} \includegraphics[width=.5\textwidth]{graph-2} \end{center} \caption{\label{fig:graph}A diagram of the relationship between \R{} graphics packages related to this article.} \end{figure} It is possible to write new graphics devices for \R{}. For example, the \pkg{RSvgDevice} package creates a new device for \R{} (to export output to an \svg{} format). However, the \pkg{gridSVG} package does not implement an \R{} graphics device; it works directly from \pkg{grid} instead. The main reason for this design choice is that the information received by an \R{} graphics device is too low-level, so too much information about the plot is lost. An \R{} graphics device is designed only for rendering output, so it only has the ability to record simple graphical primitives, such as a line segment, or a circle, or piece of text. When we draw a plot in \R{}, the graphics device receives only a series of calls to draw the individual shapes and text; the graphics device has no idea whether, for example, an individual line segment represents a tick mark, a line between data values, or a reference grid line. To demonstrate this point, consider the following code, which produces a simpler version of the dotplot from Figure \ref{fig:lattice} (see Figure \ref{fig:lattice-simple}). <<>>= barleySample <- subset(barley, site == "Duluth" & year == 1931 & as.numeric(variety) < 2) @ <>= print(dotplot(variety ~ yield, barleySample)) @ \begin{figure} \begin{center} \includegraphics[width=.8\textwidth]{gridsvg-lattice-simple} \end{center} \caption{\label{fig:lattice-simple}A simpler version of the \pkg{lattice} dotplot from Figure \ref{fig:lattice}.} \end{figure} Figure \ref{fig:device} shows the set of graphical primitives that a graphics device is instructed to draw in order to render the plot in Figure \ref{fig:lattice-simple}. We can see that by the time the information reaches the graphics device, it has been reduced to a series on lines, text, and a circle (for the data symbol). \begin{figure} \begin{boxedminipage}{\textwidth} <>= # graphics engine display list # temp <- recordPlot() # lapply(temp[[1]], function(x) { x[[2]][[1]]$name }) # graphics.off() source("dummyDev.R") dummyDev() <> dev.off() @ <>= cat(readLines("dummy.txt"), sep="\n") @ \end{boxedminipage} \caption{\label{fig:device}A representation of the information that an \R{} graphics device receives when it draws the plot from Figure \ref{fig:lattice-simple}.} \end{figure} If we are only interested in rendering the plot, this loss of information is not a problem. However, the \pkg{gridSVG} package is interested in retaining more information about the plot for two reasons: we want to be able to identify meaningful elements of the plot so that we can enhance a specific element (e.g., add a hyperlink to an axis label); and we want to be able to identify higher level groupings of elements so that we can apply modifications to coherent groups of elements (e.g., add a hyperlink to an all elements that make up an axis in the plot). Note that \pkg{RSVGTipsDevice} inserts additional information into the stream of calls to the graphics device in order to enhance specific elements, but this only works if you are the one doing the drawing. Note that \pkg{SVGAnnotation} provides clever tools to reverse-engineer the information the graphics device receives and recover information about what part of the plot each graphical primitive relates to. This has the benefit of working for all \R{} plots, but the complexity makes this solution less reliable and harder to extend. We could say that \pkg{SVGAnnotation} gets a solution from forensic evidence, whereas \pkg{gridSVG} has a signed confession to work with. The next section looks at how the \pkg{grid} package retains more information about a plot and why that is useful for exporting to \svg{}. \subsection[The grid display list]{The \pkg{grid} display list} The previous section demonstrated how drawing a plot, with a single call to the \code{dotplot()} function from the \pkg{lattice} package, produces a long list of individual calls to a graphics device in the \pkg{grDevives} package to draw a set of graphical primitives. In between \pkg{lattice} and \pkg{grDevices} lies the \pkg{grid} package; the call to \code{dotplot()} in \pkg{lattice} results in multiple calls to functions in \pkg{grid}, which in turn produce even more calls to the graphics device in \pkg{grDevices} to draw graphical primitives. When a plot is drawn with the \pkg{grid} package, in addition to low-level information being sent to an \R{} graphics device about graphical primitives, a record is kept by \pkg{grid} of the objects that it was asked to draw. This record is called the \pkg{grid} \dfn{display list}. Figure \ref{fig:grid-dl} shows the list of objects recorded by \pkg{grid} for the plot in Figure \ref{fig:lattice-simple}. This demonstrates one way in which information about the plot can be retained: \dfn{labelling}. Although \pkg{grid} has only recorded low-level shapes and text representing the plot, each of those objects has a useful label. For example, the text ``Svansota'' on the y-axis tick label has the name \code{plot\_01.ticklabels.left.panel.1.1}. This makes it possible to identify specific elements of the plot because we can identify them by name. \begin{figure} \begin{boxedminipage}{\textwidth} <>= <> grid.ls(full=TRUE) @ \end{boxedminipage} \caption{\label{fig:grid-dl}The objects that \pkg{grid} records when it draws the plot from Figure \ref{fig:lattice-simple}.} \end{figure} Note that we still ideally want the labelling scheme to be predictable AND public. Another way in which greater information about the plot can be retained is through \dfn{structure}. In order to demonstrate this idea, the following code produces a \pkg{ggplot2} plot similar to the \pkg{lattice} plot from Figure \ref{fig:lattice-simple} (see Figure \ref{fig:ggplot2-simple}). <<>>= library(ggplot2) @ <>= print(qplot(yield, variety, data=barleySample) + theme(axis.title.y=element_blank())) @ \begin{figure} \begin{center} \includegraphics[width=.8\textwidth]{gridsvg-ggplot2-simple} \end{center} \caption{\label{fig:ggplot2-simple}A \pkg{ggplot2} version of the simple \pkg{lattice} dotplot from Figure \ref{fig:lattice-simple}.} \end{figure} Figure \ref{fig:ggplot2-dl} shows the list of \pkg{grid} objects that are recorded when drawing the \pkg{ggplot2} plot in Figure \ref{fig:ggplot2-simple}. This list of objects is similar to the list from the \pkg{lattice} plot (Figure \ref{fig:grid-dl}) because almost all of the objects are labelled with useful names. However, in addition to that labelling, this list of objects also has a hierarchical structure (indicated by indenting in Figure \ref{fig:ggplot2-dl}). For example, the object \code{panel.3-4-3-4} is a collection of objects within the main plot region, consisting of a background grid, that is itself a collection of rectangles and lines, and a ``points'' object representing the data symbol on the plot. \begin{figure} \begin{boxedminipage}{\textwidth} <>= <> grid.ls(full=TRUE) @ \end{boxedminipage} \caption{\label{fig:ggplot2-dl}The objects that \pkg{grid} records when it draws the plot from Figure \ref{fig:ggplot2-simple}.} \end{figure} The fact that the structure of the plot has been recorded, in addition to labelling, makes it possible to enhance higher-level components of the plot all at once. This retention of labelling and structure is one reason why the \pkg{gridSVG} package works directly off the \pkg{grid} package, rather than working off the \pkg{grDevices} package like normal \R{} graphics devices. This design is the basis for the \pkg{gridSVG} functions that can be used to enhance a plot with \svg{} features (see Section \ref{sec:enhance}). % The following code demonstrates how we would produce an image with % \pkg{grid} in a \pdf{} format (see Figure \ref{fig:grid-pdf}). % % <>= % pdf("grid.pdf", width=3, height=1) % grid.rect(x=.2, width=.4, height=.8, just="left", % gp=gpar(col=NA, fill="lightgreen")) % grid.rect(x=.2, width=.6, height=.8, just="left") % dev.off() % @ % % \begin{figure} % \begin{center} % \includegraphics[width=3in]{grid} % \end{center} % \caption{\label{fig:grid-pdf}A simple \pkg{grid} image in a \pdf{} format.} % \end{figure} Of course, we are reliant on higher-level plotting packages injecting labelling and structure when they call \pkg{grid} functions; fortunately both \pkg{lattice} and \pkg{ggplot2} both fulfill at least one of these requirements. Note that packages like \pkg{rCharts} retain plot information at a higher level still by exporting a description of the plot itself, then using a \js{} library to do the rendering. This abandons \R{} plot functions altogether, for better and worse. \subsection[The grid viewport tree]{The \pkg{grid} viewport tree} In addition to recording the objects that it has drawn, the \pkg{grid} package also records \dfn{viewports} that are created during drawing. Viewports are used in \pkg{grid} to restrict drawing to a rectangular subset of the page. Viewports have their own coordinate systems, which means that drawing within a viewport can ignore the location of the viewport on the page. This makes it easier to create plots like the multi-panelled conditioning plots in \pkg{lattice}, where there are lots of subregions on the page, each with its own coordinate system. Figure \ref{fig:lattice-viewports} shows the viewports that are created when drawing the simple \pkg{lattice} plot in Figure \ref{fig:lattice-simple}. <>= <> downViewport("plot_01.toplevel.vp") grid.rect(gp=gpar(col=NA, fill=rgb(1,0,0,.2))) grid.text("plot_01.toplevel.vp", x=unit(1, "line"), just="left", y=unit(1, "npc") - unit(1, "line"), gp=gpar(col="red")) downViewport("plot_01.panel.1.1.off.vp") grid.rect(gp=gpar(col=rgb(1,0,0,.5), lwd=3, fill=rgb(1,0,0,.2))) grid.text("plot_01.panel.1.1.vp", x=unit(1, "line"), just="left", y=unit(1, "npc") - unit(1, "line"), gp=gpar(col="red")) upViewport() downViewport("plot_01.xlab.vp") grid.rect(gp=gpar(col=rgb(1,0,0,.5), lwd=3, fill=rgb(1,0,0,.2))) grid.text("plot_01.xlab.vp", x=unit(1, "line"), just="left", gp=gpar(col="red")) @ \begin{figure} \begin{center} \includegraphics[width=.8\textwidth]{gridsvg-lattice-viewports} \end{center} \caption{\label{fig:lattice-viewports}A diagram showing (in red) some of the viewports produced when drawing the plot from Figure \ref{fig:lattice-simple}. The viewport \code{"plot\_01.toplevel.vp"} is used to position drawing within the whole page. The viewport \code{"plot\_01.panel.1.1.vp"} is used to draw within the main plot region. The viewport \code{"plot\_01.xlab.vp"} is used to draw the x-axis label.} \end{figure} Figure \ref{fig:grid-viewports} shows a more detailed view of the \pkg{grid} display list, with viewports included. This listing has two important features: viewports are labelled, just like the objects representing shapes and text that were drawn; and viewports provide structure to the image because each object is drawn within a specific viewport. \begin{figure} \begin{boxedminipage}{\textwidth} <>= <> grid.ls(view=TRUE, full=TRUE) @ \end{boxedminipage} \caption{\label{fig:grid-dl}The objects \emph{and viewports} that \pkg{grid} records when it draws the plot from Figure \ref{fig:lattice-simple}.} \end{figure} The \pkg{grid} viewport tree retains further labelled structural information about a plot, so this is another reason why the \pkg{gridSVG} package works directly off \pkg{grid}. The significance of this design will become more apparent in the next section as we look at how \pkg{gridSVG} exports plot information from \pkg{grid} to an \svg{} file. \subsection[The svg() graphics device]{The \code{svg()} graphics device} Another reason why the \pkg{gridSVG} package works off the \pkg{grid} package rather than the \pkg{grDevices} package is to obtain complete control over the \svg{} code that is generated. To understand why \pkg{gridSVG} is designed this way, we need to look closer at the standard \code{svg()} graphics device in the \pkg{grDevices} package. We have already seen that, when we call a \pkg{lattice} function like \code{dotplot()} to draw a plot, an \R{} graphics device only gets low-level information about which graphical primitives to draw. Another issue is how the graphics device exports those graphical primitives in an \svg{} format. The \code{svg()} graphics device uses the \cairo{} graphics library (REF) to generate \svg{} code. The following code shows explicitly how we would create an \svg{} file for the \pkg{lattice} plot in Figure \ref{fig:lattice-simple} using the \code{svg()} graphics device and Figure \ref{fig:cairo-svg} shows a slightly sanitised version of the \svg{} code that results. <>= svg("lattice-simple.svg") <> dev.off() @ <>= library(XML) svg <- xmlParse("lattice-simple.svg") surface <- getNodeSet(svg, "//svg:g[contains(@id, 'surface')]", namespaces=c(svg="http://www.w3.org/2000/svg"))[[1]] xpathApply(surface, "descendant::*[@style]", function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[names(attrs) != "style"] }) saveXML(surface, "lattice-simple-bits.xml") temp <- readLines("lattice-simple-bits.xml") writeLines(gsub("([0-9]+)[.][0-9]+", "\\1", temp), "lattice-simple-bits.xml") @ \begin{figure} \begin{boxedminipage}{\textwidth} {\scriptsize <>= cat(readLines("lattice-simple-bits.xml"), sep="\n") @ } \end{boxedminipage} \caption{\label{fig:cairo-svg}The \svg{} code that is produced by the \code{svg()} graphics device when exporting the plot from Figure \ref{fig:lattice-simple}. The \svg{} code has been trimmed and tidied to make it easier to view.} \end{figure} There are two main features to identify in this \svg{} code: almost all shapes in the plot have been exported as \code{} elements (e.g., the circular data symbol has become the \code{} element on the fourth-to-last line in Figure \ref{fig:cairo-svg}); and text in the plot has been exported as ``glyphs'' (each \code{} element represents a letter), which are essentially shapes representing each character (paths again). The advantage of using \cairo{} to generate \svg{} code like this is that the output looks exactly the same as the output on screen, including fonts. The disadvantage is that even more information about the original plot has been lost. It is very difficult to recognise components of the original plot within this \svg{} code. Again, the \pkg{SVGAnnotation} package has clever functions to retrieve information about the original plot even from this faded evidence. And again, it can do this for any \R{} plot. But reliability and extensibility are more difficult. \subsection[The gridsvg graphics device]{The \code{gridsvg()} graphics device} Another important design feature of the \pkg{gridSVG} package is that it generates its \svg{} code directly (using the \pkg{XML} package [REF]). The following code shows explicitly how we would create an \svg{} file for the simple \pkg{lattice} plot in Figure \ref{fig:lattice-simple} using \pkg{gridSVG} and Figure \ref{fig:gridsvg-svg} shows the \svg{} code that results. <>= gridsvg("lattice-simple-gridsvg.svg") <> dev.off() @ <>= gridsvg("lattice-simple-gridsvg.svg", addClasses=TRUE, usePaths="none", uniqueNames=FALSE) <> dev.off() @ <>= library(XML) svg <- xmlParse("lattice-simple-gridsvg.svg") topg <- getNodeSet(svg, "//svg:g[@id = 'gridSVG']", namespaces=c(svg="http://www.w3.org/2000/svg"))[[1]] defs <- getNodeSet(topg, "//svg:defs[./svg:clipPath]", namespaces=c(svg="http://www.w3.org/2000/svg")) lapply(defs, function(x) { removeChildren(xmlParent(x), kids=list(x)) }) unwantedAttrs <- c("text-anchor", "opacity", "stroke", "fill", "stroke-opacity", "fill-opacity", "font-size", "font-weight", "font-style", "font-family", "stroke-dasharray", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "transform", "clip-path") xpathApply(topg, "descendant-or-self::*", function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% unwantedAttrs] }) sanitizeText <- function(x) { ggp <- xmlParent(xmlParent(x)) gggp <- xmlParent(ggp) removeNodes(ggp) addChildren(gggp, kids=list(x)) } xpathApply(topg, "//svg:text", namespaces=c(svg="http://www.w3.org/2000/svg"), sanitizeText) trimEmptyVP <- function(x) { children <- xmlChildren(x) if (length(children) == 0) { removeChildren(xmlParent(x), kids=list(x)) } } xpathApply(topg, "//svg:g[@class = 'viewport']", namespaces=c(svg="http://www.w3.org/2000/svg"), trimEmptyVP) saveXML(topg, "lattice-simple-gridsvg-bits.xml") temp <- readLines("lattice-simple-gridsvg-bits.xml") useLine <- grep(">= cat(readLines("lattice-simple-gridsvg-bits.xml"), sep="\n") @ } \end{boxedminipage} \caption{\label{fig:gridsvg-svg}The \svg{} code that is produced by \pkg{gridSVG} when exporting the plot from Figure \ref{fig:lattice-simple}. The \svg{} code has been trimmed and tidied to make it easier to view.} \end{figure} The most important feature of this code is that \emph{both} structure and labelling from \pkg{grid} have been retained in the \svg{} code. For example, elements have \code{id} attributes that reflect the names of the corresponding \pkg{grid} objects and viewports, elements are nested to reflect the nesting of objects within viewports (or objects within objects), different objects have been exported as different elements (e.g., there are \code{} and \code{} and \code{} elements), and some elements have \code{class} attributes that reflect the \R{} classes of the corresponding \pkg{grid} objects and viewports. This design provides the basis for manipulating an \svg{} plot that has been created with \pkg{gridSVG} (e.g., adding interaction with \js{} code; see Section \ref{sec:manipulate}). {\it NOTE the use of \code{addClasses=TRUE, usePaths="none", uniqueNames=FALSE} to get this listing in addition to the tidying and sanitising to make it small enough to (just) fit on the page.} Although we have shown that \pkg{gridSVG} exports \svg{} code with deliberate structure and labelling, in order for that structure and labelling to be useful, it is necessary to describe in more detail the algorithm used by \pkg{gridSVG} to generate \svg{} code so that we can predict the structure and labelling of output. That is the purpose of the following sections. \subsection[Exporting grid objects]{Exporting \pkg{grid} objects} The simplest type of export involves a single \pkg{grid} shape. For example, the following \pkg{grid} code draws a single rectangle (that fills the whole page). <>= grid.rect(name="r") @ This rectangle produces the following \svg{} code when exported with \pkg{gridSVG}. <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg rect <- getNodeSet(svg, "//svg:g[./svg:rect]", namespaces=c(svg="http://www.w3.org/2000/svg")) cat(saveXML(rect[[1]])) @ The most obvious rule in play here is that a \pkg{grid} rectangle object is exported as an \svg{} \code{} element. Also, the \code{id} attribute of the \code{rect} element is based on the \pkg{grid} object name (\code{"a"} becomes \code{"a.1.1"}). Table \ref{tab:grid-objects} shows the mapping from different \pkg{grid} objects to \svg{} elements. \begin{table} \caption{\label{tab:grid-objects}The mapping from \pkg{grid} objects to \svg{} elements.} \begin{center} \begin{tabular}{l l} \pkg{grid} function & \svg{} element \\ \hline \multicolumn{2}{c}{\it simple cases} \\ \code{grid.rect()} & \code{} \\ \code{grid.circle()} & \code{} \\ \code{grid.lines()} & \code{} \\ \code{grid.polyline()} & \code{} \\ \code{grid.segments()} & \code{} \\ \code{grid.polygon()} & \code{} \\ \code{grid.path()} & \code{} \\ \multicolumn{2}{c}{\it cases that require more explanation} \\ \code{grid.xspline()} & \code{} or \code{} \\ \code{grid.text()} & \code{} \\ \code{grid.raster()} & \code{} \\ \code{grid.points()} & \code{} and \code{}\\ \code{arrow()} & \code{} \\ \code{viewport()} & \code{} \\ \code{gTree()} & \code{} \\ \hline \end{tabular} \end{center} \end{table} The second general rule is that, even when exporting a single shape, the exported \svg{} element is nested within a \code{} (group) element. The reason for this is because a single \pkg{grid} object can produce more than one \svg{} element. For example, the following \R{} code draws two rectangles, which exports to two \svg{} elements (as shown below the \R{} code). <>= grid.rect(width=1:2/3, name="r") @ <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg rect <- getNodeSet(svg, "//svg:g[./svg:rect]", namespaces=c(svg="http://www.w3.org/2000/svg")) cat(saveXML(rect[[1]])) @ That example also demonstrates the basic mapping of \pkg{grid} object names to \svg{} \code{id} attributes: the parent \code{} has an \code{id} corresponding to the object name, with a numeric suffix to ensure uniqueness (in case the same object name is used anywhere else in the image); and the individual \code{} elements have that id with a further numeric suffix. These basic rules cover the ``simple cases'' in Table \ref{tab:grid-objects}, but the remaining objects require more explanation. A very simple exception involves Xspline objects, which draw smooth curves relative to a set of control points. There is no \svg{} element that corresponds to an Xspline, so the Xspline object is converted to a series of straight line segments before export. The following \R{} and \svg{} code demonstrate the basic pattern. <>= x <- c(0.25, 0.25, 0.75) y <- c(0.25, 0.75, 0.75) grid.xspline(x, y, shape=1, name="x") @ <>= grid.newpage() pushViewport(viewport(width=unit(1, "pt"), height=unit(1, "pt"))) <> doc <- grid.export(NULL) svg <- doc$svg xspline <- getNodeSet(svg, "//svg:g[./svg:polyline]", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(xspline[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("stroke-width", "fill", "fill-opacity")] }) ) out <- strsplit(saveXML(xspline[[1]]), "\n")[[1]] out[2] <- gsub("(([0-9,.]+ ){3}).+", "\\1... />", gsub(" points=", paste0("\n", paste0(rep(" ", 12), collapse=""), "points="), out[2])) cat(out, sep="\n") @ It is also possible for Xsplines to be closed curves, in which case they export to \svg{} \code{} elements instead. Another exception involves text objects. The following \R{} and \svg{} code demonstrate the basic pattern. <>= grid.text("hello", name="t") @ <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg text <- getNodeSet(svg, "//svg:g[contains(@id, 't')]", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(text[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("stroke-width", "fill", "fill-opacity")] }) ) cat(saveXML(text[[1]])) @ There are two additional nested \code{} elements that are necessary to locate and orientate the text correctly (and there are two, rather than one, to make it easier for \pkg{gridSVG} to animate text; see Section \ref{sec:enhance}). The \code{} element also has a \code{} element within it, even when there is only one line of text, to consistently allow for multi-line text. The naming of the extra components is handled with descriptive suffixes (e.g., \code{"scale"}), plus a further numeric suffix on \code{} elements to allow for multi-line text. Another complication with text in \R{} graphics is the fact that the ``text'' may in fact be an \R{} expression, which \R{} interprets as a mathematical formula. This so-called ``plotmath'' text is exported as (presentation) \mathml{}, a separate \xml{} dialect designed for displaying mathematical expressions. The following code provides a simple example that draws Euler's identity and the resulting \svg{} plus \mathml{} fragment is shown in Figure \ref{fig:mathml} (which also illustrates that \mathml{} is extremely verbose). <>= grid.text(expression(e^{i*pi} + 1 == 0), name="t") @ \lstset{ basicstyle=\ttfamily, mathescape } \begin{figure} \begin{boxedminipage}{\textwidth} {\scriptsize <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg text <- getNodeSet(svg, "//svg:g[@id = 't.1']", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(text[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("stroke-width", "fill", "fill-opacity")] }) ) temp <- saveXML(text[[1]]) # A little tinkering to avoid problems with UNICODE characters Sys.setlocale("LC_CTYPE", "en_NZ.UTF-8") temp <- gsub("\u2062", "", gsub("\u03C0", "$\\\\pi$",temp)) temp <- splitLines(temp, ">= cat(temp) @ \end{lstlisting} } \end{boxedminipage} \caption{\label{fig:mathml}The combination of \svg{} code and \mathml{} code that is produced by \pkg{gridSVG} when exporting a ``plotmath'' expression. The \svg{} code has been trimmed and tidied to make it easier to view.} \end{figure} The export of raster objects is similar to the text exception because it involves additional nested \code{} elements to locate and orientate the raster image. <>= grid.raster(matrix(0:1), name="r") @ <>= grid.newpage() pushViewport(viewport(width=unit(2, "pt"), height=unit(2, "pt"))) <> doc <- grid.export(NULL) svg <- doc$svg raster <- getNodeSet(svg, "//svg:g[contains(@id, 'r.')]", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(raster[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("stroke-width", "fill", "fill-opacity")] }) ) out <- strsplit(saveXML(raster[[1]]), "\n")[[1]] out[4] <- gsub("([A-Za-z0-9]{20}).+", "\\1... />", gsub("xlink:href=", paste0("\n", paste0(rep(" ", 13), collapse=""), "xlink:href="), out[4])) cat(out, sep="\n") @ Exporting points objects (data symbols) are complicated because some data symbols do not correspond to a simple shape. For efficiency, consistency, and to assist with animation, data symbol shapes are predefined within a \code{} element as \code{} elements, then point objects are exported as \code{} elements that reference the appropriate \code{}. The following \R{} and \svg{} code demonstrate the general pattern. <>= grid.points(.5, .5, name="p") @ <>= grid.newpage() pushViewport(viewport()) <> doc <- grid.export(NULL) svg <- doc$svg def <- getNodeSet(svg, "//svg:defs", namespaces=c(svg="http://www.w3.org/2000/svg")) point <- getNodeSet(svg, "//svg:g[contains(@id, 'p.')]", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(point[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("stroke-width", "fill", "fill-opacity")] }) ) cat(saveXML(def[[1]]), "\n") out <- strsplit(saveXML(point[[1]]), "\n")[[1]] out[2] <- gsub(" width=", paste0("\n", paste0(rep(" ", 7), collapse=""), "width="), out[2]) cat(out, sep="\n") @ All line shapes in \pkg{grid} can have an arrow drawn at either end. These arrows are exported as \code{} elements within a \code{} element (much like data symbols), then used on an exported line via a \code{marker-start} or \code{marker-end} attribute on the exported line element. The following \R{} code and \svg{} code demonstrate the basic pattern with a simple line segment that has an arrow at the end. <>= grid.segments(arrow=arrow(), name="s") @ <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg def <- getNodeSet(svg, "//svg:defs", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible(removeChildren(def[[1]], kids=xmlChildren(def[[1]])[1])) arrow <- getNodeSet(svg, "//svg:g[contains(@id, 's.')]", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(arrow[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("markerUnits", "fill", "orient")] }) ) out <- strsplit(saveXML(arrow[[1]]), "\n")[[1]] out[3] <- gsub(" markerWidth=", paste0("\n", paste0(rep(" ", 12), collapse=""), "markerWidth="), out[3]) out <- gsub(" marker-end=", paste0("\n", paste0(rep(" ", 12), collapse=""), "marker-end="), out) cat(out, sep="\n") @ {\bf Mention that missing values (\code{NA}s) in (x, y) values can produce distinct lines or polygons or paths and that these are named with a letter suffix.} Viewports and gTrees both provide hierarchical structure to a \pkg{grid} drawing and this is reflected in the \svg{} output by nesting within a parent \code{} element. The following \R{} and \svg{} code demonstrate the basic pattern for a rectangle that is drawn as the (only) child of a gTree, which is drawn within a viewport. <>= pushViewport(viewport(name="v")) grid.draw(gTree(children=gList(rectGrob(name="r")), name="g")) @ <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg nest <- getNodeSet(svg, "//svg:g[contains(@id, 'v.1')]", namespaces=c(svg="http://www.w3.org/2000/svg")) cat(saveXML(nest[[1]])) @ \subsection{Controlling the naming scheme} We have so far described features of the default naming scheme that is used when exporting the names of \pkg{grid} objects to \code{id} attributes on \svg{} elements. This section describes several ways in which that basic naming scheme can be modified using arguments to the \code{gridsvg()} function (or the \code{grid.export()} function. The simplest adjustment that we can make is to add a prefix to \emph{all} \code{id} attributes, via the \code{prefix} argument. This is useful to ensure uniqueness of \code{id} attributes when the resulting \svg{} image is included in an \html{} file along with other \svg{} images. For example, the following \R{} code draws a single rectangle. <>= grid.rect(name="r") @ The following code demonstrates how we could export that rectangle to \svg{} with a prefix of \code{plot1-} added to all \code{id} attributes, and the \svg{} code that results is shown below. <>= grid.export(prefix="plot1-") @ <>= grid.newpage() <> doc <- grid.export(NULL, prefix="plot1-") svg <- doc$svg rect <- getNodeSet(svg, "//svg:g[./svg:rect]", namespaces=c(svg="http://www.w3.org/2000/svg")) cat(saveXML(rect[[1]])) @ Another way to ensure uniqueness, at the expense of much longer \code{id} values, is to employ the \code{usePaths} argument. This can be useful when two plots of the same sort (e.g., two \pkg{lattice} plots) are drawn together as part of the same \svg{} image. The following code draws a gTree and then exports it with \code{usePaths="gPaths"} so that the \code{id} of the exported elements reflect not just the name of the object itself, but the name of the parent of the object as well. <>= grid.draw(gTree(children=gList(rectGrob(name="r")), name="g")) @ <>= grid.export(usePaths="gPaths") @ <>= grid.newpage() <> doc <- grid.export(NULL, usePaths="gPaths") svg <- doc$svg nest <- getNodeSet(svg, "//svg:g[contains(@id, 'g.1')]", namespaces=c(svg="http://www.w3.org/2000/svg")) cat(saveXML(nest[[1]])) @ The \code{uniqueNames} argument works in the other direction, by simplifying the \code{id} attributes. If it is known that the names of all objects are already unique, then we can drop the numeric suffix that is by default appended to all \code{id} attributes by specifying \code{uniqueNames=FALSE}. <>= grid.rect(name="r") @ <>= grid.export(uniqueNames=FALSE) @ <>= grid.newpage() <> doc <- grid.export(NULL, uniqueNames=FALSE) svg <- doc$svg rect <- getNodeSet(svg, "//svg:g[./svg:rect]", namespaces=c(svg="http://www.w3.org/2000/svg")) cat(saveXML(rect[[1]])) @ Finally, it is possible to export a \code{class} attribute in addition to the \code{id} attribute. This attribute directly reflects the class attribute from the \pkg{grid} object. For example, the following code shows an Xspline object exported to \svg{} with \code{addClasses=TRUE}. This allows us to identify the \code{} element as an Xspline by the \code{class} attribute of the parent \code{} element. <>= x <- c(0.25, 0.25, 0.75) y <- c(0.25, 0.75, 0.75) grid.xspline(x, y, shape=1, name="x") @ <>= grid.export(addClasses=TRUE) @ <>= grid.newpage() pushViewport(viewport(width=unit(1, "pt"), height=unit(1, "pt"))) <> doc <- grid.export(NULL, addClasses=TRUE) svg <- doc$svg xspline <- getNodeSet(svg, "//svg:g[./svg:polyline]", namespaces=c(svg="http://www.w3.org/2000/svg")) invisible( xpathApply(xspline[[1]], "descendant-or-self::*", namespaces=c(svg="http://www.w3.org/2000/svg"), function(x) { attrs <- xmlAttrs(x) removeAttributes(x) xmlAttrs(x) <- attrs[!names(attrs) %in% c("stroke-width", "fill", "fill-opacity")] }) ) out <- strsplit(saveXML(xspline[[1]]), "\n")[[1]] out[2] <- gsub("(([0-9,.]+ ){3}).+", "\\1... />", gsub(" points=", paste0("\n", paste0(rep(" ", 12), collapse=""), "points="), out[2])) cat(out, sep="\n") @ \subsection{Exporting graphical parameters} In all of the examples so far, the \svg{} code that is exported by \pkg{gridSVG} has been simplified by removing all style attributes (just to remove clutter). This section looks at how graphical parameter settings in a \pkg{grid} drawing (colours, line thickness, and fonts) are exported to \svg{} style attributes. In \pkg{grid}, a complete set of graphical parameter settings are defined by the graphics device and then viewports and shapes can override individual settings (for themselves and their children) by specifying explicit new values. This is duplicated in \svg{} code by defining all style attributes in an outer \code{} element and then overriding those defaults where necessary in individual elements. For example, the following code draws a rectangle that is red (rather than the default black). <>= grid.rect(name="r", gp=gpar(col="red")) @ The following \svg{} code shows how this setting is exported to \svg{}. In this output, we are showing a little more of the entire \svg{} document that gets created by showing the \code{} element that is wrapped around all other \svg{} output in the document. That high-level \code{} element contains default settings for all style elements, then the \code{} element only sets its colour to red. Both \code{stroke} and \code{stroke-opacity} are set because this is an \emph{opaque} red. <>= grid.newpage() <> doc <- grid.export(NULL, uniqueNames=FALSE) svg <- doc$svg rect <- getNodeSet(svg, "//svg:g[@id = 'gridSVG']", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(rect[[1]]), '>= grid.text("hello", name="t", gp=gpar(fontfamily="mono")) @ The \svg{} code below shows the \code{} element that is produced from the text object and we can see that the \code{font-family} attribute contains a list of fonts for a web browser to try. <>= grid.newpage() <> doc <- grid.export(NULL, uniqueNames=FALSE) svg <- doc$svg text <- getNodeSet(svg, "//svg:text", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(text[[1]]), '>= grid.rect(name="r") @ <>= grid.export(annotate=TRUE) @ <>= grid.newpage() <> doc <- grid.export(NULL, uniqueNames=FALSE) svg <- doc$svg meta <- getNodeSet(svg, "//metadata") temp <- splitLines(saveXML(meta[[1]]), 'gridsvg:generator', " time", " name=") cat(temp, sep="\n") @ \subsection{Full disclosure} All of the examples of \svg{} output that have been shown so far have been trimmed and tidied to some extent in order to reduce clutter and fit the limits of space. This section provides a simple demonstration of the complete, unadulterated \svg{} output for a very simple \pkg{grid} drawing. The following code draws a single rectangle and Figure \ref{fig:gridsvg-svg-full} shows the complete \svg{} code that is produced when the rectangle is exported with \pkg{gridSVG}. <>= grid.rect(name="r") @ One feature of the code in Figure \ref{fig:gridsvg-svg-full} that has not been seen previously is the top-level \code{} element that contains a transformation to switch from \svg{}'s native coordinate system with $(0, 0)$ at top-left to a more natural (for \R{}) system where $(0, 0)$ is at bottom-left. \begin{figure} \begin{boxedminipage}{\textwidth} {\scriptsize <>= grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg txt <- splitLines( splitLines( splitLines( splitLines( splitLines( splitLines(saveXML(svg), ">= rt() grid.filter("r", filterEffect(feGaussianBlur(sd=3))) @ <>= pdf("filter.pdf", width=2, height=2) <> dev.off() @ \begin{figure} \begin{center} \includegraphics[width=1in]{filter} \hspace{1in} \includegraphics[width=1in]{filter-svg-crop} \end{center} \caption{\label{fig:filter}A \pkg{grid} scene consisting of a single \code{"rect"} grob, with an \svg{} gaussian blur filter effect applied to it. On the left, the image is drawn on a standard \R{} \pdf{} graphics device, so the gaussian blur has no visible effect. On the right, the image has been exported to \svg{}, so the gaussian blur has an effect.} \end{figure} On a standard \R{} graphics device, the \svg{} gaussian blur has no effect (see the left rectangle in Figure \ref{fig:filter}). However, \pkg{grid} has added filter information to the rectangle, as we can see in the output from \code{grid.ls()} below; the grob in this image is not just a normal \code{"rect"} grob, it is a \code{"filtered.grob"}. <>= grid.ls(fullNames=TRUE) @ <>= grid.newpage() <> grid.ls(fullNames=TRUE) @ When we export this image to \svg{}, the filter effect is exported along with the rectangle. The right-hand rectangle in Figure \ref{fig:filter} shows the \svg{} image that results, with the rectangle now blurred. The code below shows the raw \svg{} code for the image so that we can see the filter information that has been exported. This code will be explained in more detail later in Section \ref{secton:filtereffects}; for now, the important point is that, in addition to a \code{} element, there is a \code{} element in the \svg{} code. <>= gridsvg("filter.svg", width=2, height=2) <> dev.off() grid.newpage() <> doc <- grid.export(NULL) svg <- doc$svg filter <- getNodeSet(svg, "//svg:defs", namespaces=c(svg="http://www.w3.org/2000/svg")) rect <- getNodeSet(svg, "//svg:g[./svg:rect]", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(filter[[1]]), '>= temp <- readLines("filter.svg") writeLines(gsub('encoding="ASCII"', "", temp), "filter.svg") # 50.8 comes from 144px / 72dpi (pdf dpi?) * 25.4 (mm conversion) # NOTE that 504 comes from 7in * 72dpi (PDF device resolution) system("wkhtmltopdf --page-width 50.8 --page-height 50.8 filter.svg filter-svg.pdf") system("pdfcrop filter-svg.pdf filter-svg-crop.pdf") @ Table \ref{table:svgfeatures} shows the list of functions that can be used to add \svg{} features to a \pkg{grid} image. The following sections describe each function in more detail. \begin{table} \caption{\label{table:svgfeatures}Functions that can be used to add \svg{} features to a \pkg{grid} image.} \begin{tabular}{l l} \code{grid.hyperlink()} & Add a hyperlink to an object \\ \code{grid.animate()} & Animate an object \\ & \\ \code{grid.filter()} & Add a filter effect to an object \\ \code{grid.gradientFill()} & Add a gradient fill to an object \\ \code{grid.patternFill()} & Add a pattern fill to an object \\ \code{grid.mask()} & Add a mask to an object \\ \code{grid.clipPath()} & Apply a clipping path to an object \\ & \\ \code{grid.garnish()} & Add an SVG attribute to an object \\ \code{grid.comment()} & Add an \svg{} comment to an image \\ \code{grid.script()} & Add a \js{} script to an image \\ \code{grid.element()} & Add an \svg{} element to an image \\ \end{tabular} \end{table} \subsection{Hyperlinks} The \code{grid.hyperlink()} function allows a hyperlink to be associated with a component of an image. For example, the following code draws a rectangle and a piece of text and then associates a hyperlink with the rectangle, identifying the rectangle by its name. <>= grid.rect(width=unit(2, "in"), height=unit(.5, "in"), gp=gpar(fill="black"), name="r") grid.text("click me!", gp=gpar(col="white")) grid.hyperlink("r", href="https://www.stat.auckland.ac.nz") @ When exported to \svg{}, an \code{} (anchor) element is added to the code so that a click on the rectangle will navigate to the Department of Statistics web site at the University of Auckland, as shown in the \svg{} code below. Live versions of this example, and other examples in this section, are provided on the web site associated with this article (see Section \ref{section:links}). <>= grid.newpage() <> grid.export("hyperlink.svg") doc <- grid.export(NULL) svg <- doc$svg anchor <- getNodeSet(svg, "//svg:a", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(anchor[[1]]), ">= grid.rect(y=1:2/3, width=unit(2, "in"), height=unit(.5, "in"), gp=gpar(fill=c("red", "blue")), name="rects") grid.hyperlink("rects", group=FALSE, href=c("https://www.massey.ac.nz/massey/", "https://www.stat.auckland.ac.nz")) @ <>= grid.newpage() <> grid.export("hyperlinks.svg") doc <- grid.export(NULL) svg <- doc$svg rects <- getNodeSet(svg, "//svg:g[@id = 'rects.1']", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(rects[[1]]), ">= grid.circle(r=.2, name="c") grid.animate("c", r=c(.2, .4, .2), duration=3) @ The \svg{} code below shows that an \code{} element is created when the image is exported. A live version of the image is available from the web site for this article. <>= grid.newpage() <> grid.export("animate.svg") doc <- grid.export(NULL) svg <- doc$svg anim <- getNodeSet(svg, "//svg:animate", namespaces=c(svg="http://www.w3.org/2000/svg")) circle <- getNodeSet(svg, "//svg:g[./svg:circle]", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(anim[[1]]), ">= <> @ This example shows the use of the \code{filterEffect()} function to define a filter effect. The first argument to that function is either a single filter or a list of filters (an overall filter effect can be a combination of more than one filter). Table \ref{table:filtereffects} shows the list of currently supported filters. Each filter takes a different set of arguments so the individual filter function help pages and the \svg{} documentation\footnote{\url{http://www.w3.org/TR/SVG/filters.html}} should be consulted for details about each filter. Example of multiple filters ... <>= rt() grid.filter("r", filterEffect(list(feGaussianBlur(sd=3, result="blur"), feComposite("SourceGraphic", "blur", operator="over")))) @ One added flexibility with filter effects is that the scope of a filter effect can be restricted to just a subregion of the page. For example, the following code applies the same Gaussian filter to the rectangle, but limits the scope of the filter to the bottom-left corner of the page by specifying the location and size of the filter (see Figure \ref{fig:filters}). <>= rt() grid.filter("r", filterEffect(feGaussianBlur(sd=3), x=0, y=0, width=.5, height=.5, just=c("left", "bottom"))) @ Furthermore, each filter within a filter effect also has a scope \emph{and} both the filter and the filter effect scope can be specified either in normal \pkg{grid} units or in terms of the bounding box of the object that is being filtered. The following code demonstrates another variation on the Gaussian blur where the Gaussian blur filter itself is limited to the bottom-left corner \emph{of the rectangle} (so the blur is not visible \emph{outside} the bounds of the original rectangle; see Figure \ref{fig:filters}). <>= rt() grid.filter("r", filterEffect(feGaussianBlur(x=0, y=0, width=.5, height=.5, just=c("left", "bottom"), sd=.02), primitiveUnits="bbox")) @ Another feature of filter effects is the ability to separately \emph{register} a filter effect and then refer to it by name. This produces more efficient \svg{} code because the filter effect is only recorded once in the \svg{} file. The following code demonstrates the use of \code{registerFilter} to associate a filter effect with a label and then the use of the \code{label} argument to \code{grid.filter()} to refer to a registered filter effect. The registered filter effect is applied to \emph{two} grobs this time, both the rectangle and the text, but only one copy of the definition of the filter is necessary (see Figure \ref{fig:filters}). <>= rt() registerFilter("blur", filterEffect(feGaussianBlur(sd=3))) grid.filter("r", label="blur") grid.filter("t", label="blur") @ The scope of a filter effect is determined when the filter effect is registered (if a filter effect is not registered explicitly, via \code{registerFilter()}, then the filter effect is automatically registered the first time it is used in \code{grid.filter()}). This means that the scope of a filter effect is affected by the \emph{context} in which the filter effect is registered; the location and size of the filter effect scope depends on the current \pkg{grid} viewport. The following code demonstrates this point by pushing a viewport in the top-right corner of the page and then calling \code{grid.filter()} with an unregistered filter effect. The scope of the filter effect is not the entire page---it is the extent of the current viewport, which in this case is the top-right corner of the page (see Figure \ref{fig:filters}). <>= rt() pushViewport(viewport(x=1, y=1, width=.5, height=.5, just=c("right", "top"))) grid.filter("r", filterEffect(feGaussianBlur(sd=3))) @ <>= gridsvg("filter-multi.svg", width=2, height=2) <> dev.off() gridsvg("filter-partial.svg", width=2, height=2) <> dev.off() gridsvg("filter-bbox.svg", width=2, height=2) <> dev.off() gridsvg("filter-register.svg", width=2, height=2) <> dev.off() gridsvg("filter-context.svg", width=2, height=2) <> dev.off() @ <>= for (i in c("multi", "partial", "bbox", "register", "context")) { temp <- readLines(paste0("filter-", i, ".svg")) writeLines(gsub('encoding="ASCII"', "", temp), paste0("filter-", i, ".svg")) # 50.8 comes from 144px / 72dpi (pdf dpi?) * 25.4 (mm conversion) # NOTE that 504 comes from 7in * 72dpi (PDF device resolution) system(paste0("wkhtmltopdf --page-width 50.8 --page-height 50.8 filter-", i, ".svg filter-", i, "-svg.pdf")) system(paste0("pdfcrop filter-", i, "-svg.pdf filter-", i, "-svg-crop.pdf")) } @ \begin{figure} \begin{center} \includegraphics[width=1in]{filter}\\ ~\\ \includegraphics[width=1in]{filter-svg-crop}% \includegraphics[width=1in]{filter-multi-svg-crop}% \includegraphics[width=1in]{filter-partial-svg-crop}% \includegraphics[width=1in]{filter-bbox-svg-crop}% \includegraphics[width=1in]{filter-register-svg-crop}% \includegraphics[width=1in]{filter-context-svg-crop} \end{center} \caption{\label{fig:filters}Top row: a \pkg{grid} scene consisting of a rectangle grob and a text grob. Bottom row, from left to right: a \svg{} gaussian blur filter effect applied to the rectangle grob; a filter effect consisting of two filters (a composite of the source over a Gaussian blur) applied to the rectangle grob; a Gaussian filter applied to the rectangle, but with the scope of the filter effect limited to the bottom-left corner of the page; a Gaussian filter applied to the rectangle, but with the scope limited to the bottom-left corner \emph{of the rectangle}; a Gaussian filter applied to both the rectangle and the text; a Gaussian filter applied to the rectangle, but with the filter effect registered within the context of a viewport in the top-right corner of the page (which limits the scope of the filter to that viewport).} \end{figure} \begin{table} \caption{\label{table:filtereffects}The list of currently supported filter effects.} \begin{center} \begin{tabular}{l l}\hline \code{feBlend} & Blend two objects together \\ \code{feColorMatrix} & Apply a matrix transformation on colour values \\ \code{feComponentTransfer} & Perform Colour Component-wise Remapping \\ \code{feComposite} & Combine images using Porter-Duff operations \\ \code{feConvolveMatrix} & Apply a matrix convolution filter effect \\ \code{feDiffuseLighting} & Light an image using the alpha channel as a bump map \\ \code{feDisplacementMap} & Displace pixel values from a filter input \\ \code{feDistantLight} & Create a Distant Light Source \\ \code{feFlood} & Create and fill a rectangular region \\ \code{feGaussianBlur} & Apply a Gaussian blur to an image \\ \code{feImage} & Draw a referred image \\ \code{feMerge} & Composite image layers together \\ \code{feMorphology} & "Fatten" or "thin" artwork \\ \code{feOffset} & Offset an input image relative to its current position \\ \code{fePointLight} & Create a Point Light Source \\ \code{feSpecularLighting} & Light an image using the alpha channel as a bump map \\ \code{feSpotLight} & Create a Spot Light Source \\ \code{feTile} & Fill a rectangle with a tiled pattern of an input image \\ \code{feTurbulence} & Create an image using the Perlin turbulence function \\ \hline \end{tabular} \end{center} \end{table} \subsection{Gradient fills} The \code{grid.gradientFill()} function can be used to associate a linear or radial gradient fill with a component of an image. The following code shows a simple demonstration that fills a rectangle with a linear gradient (see Figure \ref{fig:gradients}). <>= grid.rect(width=0.8, height=0.8, name="r") grid.gradientFill("r", linearGradient()) @ A linear gradient is defined by a set of \dfn{gradient stops}. These consist of a set of colours, specified in the \code{cols} argument, and a set of locations, with the gradient smoothly transitioning from one colour to the next between the locations. The locations are all on a straight line, with start point controlled by arguments \code{x0} and \code{y0} and end point controlled by \code{x1} and \code{y1}. The \code{stops} argument specifies colour locations along the straight line as a proportion of the distance along the line. The following code demonstrates a more complicated linear gradient specification, with the gradient transitioning from top to bottom, starting at a dark red and passing through a lighter red then back to the dark red. <>= grid.rect(width=0.8, height=0.8, name="r") grid.gradientFill("r", linearGradient(hcl(0, 60, c(40, 80, 40)), c(0, 0.5, 1), x0=0.5, y0=1, x1=0.5, y1=0)) @ The \code{radialGradient()} function is similar, except that the location of the gradient stops are specified as a proportion of the distance from the centre to the edge of a circle, as specied by \code{x}, \code{y}, and \code{r}. The following code demonstrates a radial version of the previous example, transitioning from dark red at the centre, passing through a lighter red, then back to dark red at the edges. <>= grid.rect(width=0.8, height=0.8, name="r") grid.gradientFill("r", radialGradient(hcl(0, 60, c(40, 80, 40)), c(0, 0.5, 1), x=0.5, y=0.5, r=0.5)) @ Further fine control of the gradient fills is also possible; see the help pages for the relevant functions for details. <>= gridsvg("gradient-linear.svg", width=2, height=2) <> dev.off() gridsvg("gradient-linear2.svg", width=2, height=2) <> dev.off() gridsvg("gradient-radial.svg", width=2, height=2) <> dev.off() @ <>= for (i in c("linear", "linear2", "radial")) { temp <- readLines(paste0("gradient-", i, ".svg")) writeLines(gsub('encoding="ASCII"', "", temp), paste0("gradient-", i, ".svg")) # 50.8 comes from 144px / 72dpi (pdf dpi?) * 25.4 (mm conversion) # NOTE that 504 comes from 7in * 72dpi (PDF device resolution) system(paste0("wkhtmltopdf --page-width 50.8 --page-height 50.8 gradient-", i, ".svg gradient-", i, "-svg.pdf")) system(paste0("pdfcrop gradient-", i, "-svg.pdf gradient-", i, "-svg-crop.pdf")) } @ \begin{figure} \begin{center} \includegraphics[width=1in]{gradient-linear-svg-crop} \includegraphics[width=1in]{gradient-linear2-svg-crop} \includegraphics[width=1in]{gradient-radial-svg-crop} \end{center} \caption{\label{fig:gradients} .} \end{figure} As with filters, gradient fills can be registered with a label, using \code{registerGradientFill()}, and then referred to by label in \code{grid.gradientFill()}. Also, as with filters, the locations of gradient stops within a gradient fill can be relative to the bounding box of the grob being filled or they can be \pkg{grid} units, the meaning of which will depend upon the current viewport at the time the gradient fill is registered. \subsection{Pattern fills} The \code{grid.patternFill()} function allows a pattern fill to be associated with a component of a plot. The first argument specifies the component to fill and the second argument specifies a pattern. The following code shows a simple example, where a rectangle is filled with a ``polka dot'' pattern (see Figure \ref{fig:patterns}). <>= grid.rect(width=0.8, height=0.8, name="r") grid.patternFill("r", pattern(circleGrob(r=0.25, gp=gpar(fill="black")))) @ The \code{pattern()} function provides the means to define a fill pattern. In the simple case above, we were able to just provide this function with a filled circle grob to use as the basis of the fill pattern, but for more complex fill patterns, we need a better understanding of how the pattern is created. The first argument to \code{pattern()} can be any \pkg{grid} grob (or gTree). The \code{pattern()} function draws this grob on a temporary (off-screen) graphics device to create a \dfn{tile image}. The tile image is then replicated to produce a repetitive fill pattern. There are several parameters of this process that we can control. The positioning and size of the tile image \emph{within the current viewport context} is determined by arguments \code{x}, \code{y}, \code{width}, and \code{height}. This can be used to make the fill pattern more dense or less dense. There are also arguments \code{dev.width} and \code{dev.height} to control the size at which the tile image is created. This matters for things like line width; if the tile image is drawn at the standard graphics device size (7 inches square), but the tile image is then shrunk down to a small size to use as a pattern fill, lines can become too thin to see. The following code shows a slightly more complex pattern fill. In this case, the tile image is a gTree consisting of two overlapping coloured rectangles. The tile image is created on a graphics device only 0.5 inches square (so that lines are still visible when the tile image is shrunk) and then the tile image is drawn (as a repeating pattern) 20\% of the size of the page, starting from the centre of the page (see Figure \ref{fig:patterns}). <>= grid.rect(width=0.8, height=0.8, name="r") patternGrob <- gTree(children=gList( rectGrob(0.4, 0.6, 0.4, 0.3, gp=gpar(col="blue")), rectGrob(0.6, 0.4, 0.4, 0.3, gp=gpar(col="red")))) grid.patternFill("r", pattern(patternGrob, x=0.5, y=0.5, width=0.2, height=0.2, dev.width=0.5, dev.height=0.5)) @ As with filters and gradient fills, pattern fills can be explicitly registered and referred to by name and the location and size of the pattern is influenced by the current viewport context when the pattern is registered. <>= gridsvg("pattern-simple.svg", width=2, height=2) <> dev.off() gridsvg("pattern-gtree.svg", width=2, height=2) <> dev.off() @ <>= for (i in c("simple", "gtree")) { temp <- readLines(paste0("pattern-", i, ".svg")) writeLines(gsub('encoding="ASCII"', "", temp), paste0("pattern-", i, ".svg")) # 50.8 comes from 144px / 72dpi (pdf dpi?) * 25.4 (mm conversion) # NOTE that 504 comes from 7in * 72dpi (PDF device resolution) system(paste0("wkhtmltopdf --page-width 50.8 --page-height 50.8 pattern-", i, ".svg pattern-", i, "-svg.pdf")) system(paste0("pdfcrop pattern-", i, "-svg.pdf pattern-", i, "-svg-crop.pdf")) } @ \begin{figure} \begin{center} \includegraphics[width=1in]{pattern-simple-svg-crop} \includegraphics[width=1in]{pattern-gtree-svg-crop} \end{center} \caption{\label{fig:patterns} .} \end{figure} \subsection{Masks} A \dfn{mask} is a grey-scale image that is used to affect the opacity of a ``target image''. Any black regions in the mask make the corresponding region of the target image transparent, white regions in the mask make corresponding target regions opaque, and grey regions in the mask make correspinding target regions semitransparent. The \code{grid.mask()} object allows a mask to be applied to a component of a plot. The first argument specifies the component to be masked and the second argument specifies the mask. The following code provides a simple example, where the original scene is a filled black rectangle on a grey background, the mask is a black circle on a white background, and the mask is applied to the black rectangle, which ``punches'' a hole in the rectangle (revealing the grey background behind; see Figure \ref{fig:mask}). <>= grid.rect(gp=gpar(col=NA, fill="grey")) grid.rect(width=0.8, height=0.8, gp=gpar(fill="black"), name="r") @ <>= circleMask <- gTree(children=gList( rectGrob(gp=gpar(col=NA, fill="white")), circleGrob(r=0.2, gp=gpar(fill="black")))) grid.mask("r", mask(circleMask)) @ <>= gridsvg("mask-simple.svg", width=2, height=2) <> <> dev.off() pdf("mask-scene.pdf") <> dev.off() pdf("mask-circle.pdf") grid.draw(circleMask) dev.off() @ <>= # NOTE: wkhtmltopdf does NOT render this SVG correctly! # SO we use inkspace instead for (i in c("simple")) { temp <- readLines(paste0("mask-", i, ".svg")) system(paste0("inkscape --export-pdf=mask-", i, "-svg.pdf mask-", i, ".svg")) } @ \begin{figure} \begin{center} \includegraphics[width=1in]{mask-scene} \includegraphics[width=1in]{mask-circle} \includegraphics[width=1in]{mask-simple-svg} \end{center} \caption{\label{fig:mask} .} \end{figure} Like filters, the scope of the mask can be limited and masks can be registered and referred to by label just like filters, gradient fills, and pattern fills. It is also possible to ``push'' a mask, similar to pushing a viewport, with the \code{pushMask()} function. When a mask is pushed with this function, the mask affects all subsequent drawing, until the next \code{popMask()}, \code{popContext()}, \code{popViewport()}, or \code{upViewport()} call. \subsection{Clipping paths} The function \code{grid.clipPath()} allows a component of a plot to be clipped relative to an arbitrary path. The first argument specifies the component to clip and the second argument specifies the clipping path. The following code shows a simple example, with a piece of text clipped relative to a circle. <>= grid.rect(gp=gpar(col=NA, fill="grey")) grid.text("clip\nme!", name="t") @ <>= grid.clipPath("t", clipPath(circleGrob(r=0.15))) @ <>= gridsvg("clip-simple.svg", width=1, height=1) <> <> dev.off() pdf("clip-scene.pdf", width=1, height=1) <> dev.off() pdf("clip-circle.pdf", width=1, height=1) grid.draw(circleGrob(r=0.15)) dev.off() @ <>= for (i in c("simple")) { temp <- readLines(paste0("clip-", i, ".svg")) system(paste0("inkscape --export-pdf=clip-", i, "-svg.pdf clip-", i, ".svg")) } @ \begin{figure} \begin{center} \includegraphics[width=1in]{clip-scene} \includegraphics[width=1in]{clip-circle} \includegraphics[width=1in]{clip-simple-svg} \end{center} \caption{\label{fig:clip} .} \end{figure} As with masks, the grob used to describe the clipping path can be arbitrarily complex, although the clipping path resulting from a complex grob will typically just be the outline of the union of individual components of the grob (e.g., a complex scene bounded by a rectangle will result in a clipping path corresponding to the boundary rectangle). Like filters, gradients, patterns, and masks, clipping paths can be separately registered before use and, like masks, clipping paths can be pushed, using \code{pushClipPath()}, to apply clipping to subsequent drawing. \subsection{Garnishing} The \code{grid.garnish()} function can be used to add \svg{} attributes to a \pkg{grid} grob. This differs from the functions in previous sections because it provides almost direct access to the \svg{} code that will be generated. In previous sections, the functions generated \R{} objects that were converted to \svg{} representations, but in this section the information added to grobs does not require any transformation. The following code provides a simple demonstration of \code{grid.garnish()} that adds a \code{title} attribute to a rectangle (which will produce a tooltip in some browsers). The first argument is the name of the grob to garnish and subsequent arguments specify the attributes to add. <>= grid.rect(width=0.8, height=0.8, gp=gpar(col=NA, fill="grey"), name="r") grid.garnish("r", title="rectangle tooltip") @ The \svg{} that is generated by \code{grid.export()} is shown below. We can see the \code{title} attribute has been added to the \code{} element parent of the \code{} element. As for hyperlinks, it is possible to specify more than one attribute value and associate attributes with individual \code{} elements by using the \code{group} argument to \code{grid.garnish()}. <>= grid.newpage() <> grid.export("garnish.svg") doc <- grid.export(NULL) svg <- doc$svg anchor <- getNodeSet(svg, "//svg:g[./svg:rect]", namespaces=c(svg="http://www.w3.org/2000/svg")) temp <- splitLines(saveXML(anchor[[1]]), "} element into a scene, and \code{grid.element()} can be used to insert an arbitrary \svg{} element into a scene. The following code shows a simple example of adding a piece of javascript that will show an alert dialog when the image is loaded. The resulting \svg{} code is shown below the \R{} code. <>= grid.comment("This is a javascript script to show an alert") grid.script("alert('A javascript alert')") @ <>= grid.newpage() <