Scaling Issues

2023-12-08

Scaling SVG outputs

The SVG files produced by svglite do not include width and height properties. This is a deliberate choice intended to make it easier to fit fluidly a SVG figure to its enclosing container. The scaling straightforward but requires some understanding of the viewBox SVG attribute which is included in all SVGs produced by svglite. This property defines the aspect ratio of the plot (as well as a user coordinate system, see next section).

Fluid scaling

The viewBox is determined by the width and height arguments of svglite’s device functions (with 10’’ x 8’’ the default). Although those dimensions are supplied in inches, the viewBox’s user coordinate system is completely unit agnostic. The main effect is thus to determine an aspect ratio. Since dimensions are not provided, the dimensions of the enclosing container are used instead and the SVG is rescaled to fit the container (although Internet Explorer currently requires some CSS tricks to get this behaviour, see https://tympanus.net/codrops/2014/08/19/making-svgs-responsive-with-css/).

Aspect ratio is preserved by default when the figure is scaled up or down. The details of how the aspect ratio is preserved can be adjusted in multiple ways via the preserveAspectRatio attribute. See https://www.sarasoueidan.com/blog/svg-coordinate-systems/ for more information about this property.

Other useful resource: https://css-tricks.com/scale-svg/

Natural scaling

Another strategy is needed in order to scale the figure to make the text within the SVG consistent with the text in the surrounding web page. That could be useful, for instance, to create a consistent appearance in an HTML presentation. Since the user coordinate system defined by the viewBox is unitless, we need to map the figure to its natural dimensions. This will ensure a correspondence between the scale of the figure and that of the web page.

As mentioned above, the natural scale of svglite’s figures is in points and is determined by the width and height arguments that you supply to the device functions (10’’ x 8’’ being the default). Although those dimensions are specified in inches, the coordinate system is scaled in points. Counting 72 points per inch, the default SVG surface is thus 720 x 576 pt. Note that the CSS standard defines 12pt to be equal to 16px, the default size of text in most browsers. Since 12pt is the default text size in svglite as well, a SVG scaled to its natural dimensions will appear seamless with web text of 16px. If the text in your web page has another size, you will have to compute a scale factor and adjust the dimensions of the SVG accordingly.

To sum up, displaying a plot according to its natural dimensions requires providing the user agent with information about what the lengths defined within the SVG actually mean. There are several ways to achieve this. First you can edit the SVG and enclose it in another pair of <svg> tags that defines height and width. The root <svg> element determines the final dimensions of the figure.

A second way is to enclose the figure in a <div> tag with appropriate dimensions and let the SVG figure rescale itself to that container (cf. the section on fluid scaling):

<div style="width: 720pt; height: 576pt">
  <img src="figure.svg">
</div>

Finally, you can directly specify the dimensions in the <img> or <object> tag that is embedding the figure. Note that the dimension attributes of those tags do not accept arbitrary units, so you will have to supply the dimensions in pixels. Just multiply the width and height measured in points with a factor of 16/12:

<img src="figure.svg" width="960" height="768">

Internal notes

Device scaling

As other graphics devices, svglite is scaled in big points (1/72 inch) rather than pica points (1/72.27 inch). Note that in LaTeX and in the grid graphics system on which ggplot2 is based, points refer to pica points. Big points are denoted in LaTeX by bp and in CSS by pt. We use the latter notation. See https://tex.stackexchange.com/a/200968/19755 for some historical background about these units.

The conversion between device units and physical dimensions is determined by the DevDesc parameter ipr. IPR stands for inches per raster (native device coordinates are sometimes called rasters in R terminology) and is set to 1/72 in svglite. The device’s physical dimensions are set by the following DevDesc parameters (with width and height the plot dimensions set by the user in inches):

Parameter Value
left 0
top 0
right width * 72
bottom height * 72

A default svglite plot surface is thus 720 x 576 pt.

Scaling of graphical elements

It is conventional for the fundamental line width (lwd = 1) to correspond to a line width of 1/96 inch and svglite obeys this convention. Also, like other R graphics devices, svglite interprets all point sizes directly as big points (e.g. the ps graphical parameter and the fontsize argument of device functions). The default font size is 12pt.

Text metrics are computed by systemfonts, which uses freetype to extract metrics for each glyph and calculate string dimensions from that. Text metrics are calculated at 1000dpi based on cex * ps font size.

The Base graphics system also makes use of the obscure cra parameter and its relatives (cin, cxy, and csi). cra serves as a crude measure for a default character height and width for the default fontsize provided when the device is called (12pt in svgilte). The main effect of this parameter (more specifically, the height component) is to change the relationship between the margin parameters mar/mai and oma/omi. The margins mar and oma are specified in line units and character height is used as a measure of line spacing to convert margins measured in lines to physical margins. As in other devices, cra[0] is set to 0.9 * pointsize and cra[1] to 1.2 * pointsize. These parameters are completely unused in the Grid graphics system.

SVG output

The SVG output sets up a viewBox (a user coordinate system) with values scaled in big points.

viewBox: The width and height are set to dd->right and dd->bottom respectively (these values are determined by the user-supplied figure width and height).

Line width: 1 lwd should equal 1/96 inch. svglite gets values scaled in device coordinates (big points), so the line width is multiplied by 72/96.

Text: gdtools returns metrics scaled in big points so no transformation is needed. We do need to add px units to work around a rendering bug in Firefox. Note that when a viewBox is set up, a pixel equals one unit in the user coordinate system and thus actually represents a big point.