Specifying Fonts in R

Paul Murrell
Department of Statistics
The University of Auckland

This document discusses how R allows fonts to be specified. This is within the context of R's graphics engine -- graphics systems, such as base graphics and grid can obviously implement their own interfaces, but the engine capabilities will limit what they are able to achieve.

Currently (R 1.5.0), the following font characteristics may be set:
ps The font pointsize
cex The "character expansion" (text size = ps*cex)
font The font "face" (1=plain, 2=bold, 3=italic, 4=bold-italic)

The base graphics interface provides some extensions on this, basically allowing for multiple cex and font settings to exist for different purposes (e.g., axis labels and titles).

One problem with this font specification is that the graphics engine does not provide an easy way to specify a font "family". The windows device gets around this by allowing higher values of the font "face" to be used to map into a text file of different fonts, but this is not a terribly clean or convenient mechanism.

Another problem is that there is no way to specify a change in inter-line spacing of text (for drawing a single piece of multi-line text, e.g., "one\ntwo\nthree lines")

Specifications in other systems

Unfortunately, there exists no well-defined and universally accepted taxonomy for classifying fonts based on their names, and terms that apply to one font family name may not be appropriate for others.
(from W3C CSS2 specification)

In the W3C CSS2 specification, the following font characteristics are allowed: Font family, Font style (normal or italic), Font variant (normal or smallcaps), Font weight (normal or bold), Font stretch (sort of like cex?), and Font size.

In the GNU plotting utilities fonts are specified by family and face, where face is a number basically like R's "font", with values higher than 4 indicating other nonstandard variations on a font family (only used for Hershey fonts).

In the Java 2 SDK version 1.4 the TextAttribute class has fields for family, weight (bold), width (like the Font stretch in CSS2), posture (oblique), and size.


What I want to do is add family and lineheight characteristics to R's font specification.

I can take care of the changes to base graphics, grid, and R's graphics engine, but some general issues are: impact on users (R-level API and C-level API), and impact on device maintainers. The device maintainers would either have to make some changes or trust me to make them.

R-level changes

This can be quite minimal.

For lineheight, this can just be ignored by base graphics for now. Even if added, it would just be an additional par() value with no impact on existing code.

For family, allow the current font=number to remain, but allow as an alternative font=list(family=string, face=numberorstring). Again, no impact on existing user code.

The functions strwidth and strheight need to have arguments added to allow the specification of the font for the text being sized. (This doesn't need to happen in grid because "strwidth" and "strheight" units are always relative to the current font settings.)

C-level changes

The new lineheight and font family information has to be passed from graphics systems to the graphics engine and from the graphics engine to the graphics devices. In other words, there need to be changes to GraphicsEngine.h and GraphicsDevice.h Here are two options for doing this:

  1. Simply add additional lineheight and family arguments (and possibly rename font argument to "face") to relevant graphical primitives (e.g., GEText and dev_text).

    For devices this would only require modifying the argument list for a couple of functions and possibly renaming "font" to "face". i.e., treat the new face argument exactly like the old font argument. Lineheight would only be used in the graphics engine. Family could just be ignored until you feel like doing anything about it (although maybe could add a warning if given a non-empty family value).

  2. Change the way graphical arguments are passed to graphics engine and graphics devices more substantially; create a new gpar structure (an SEXP?) within which all graphical arguments can be passed and replace primitive-specific argument lists with a single gpar ptr argument. All graphics primitives get all graphical arguments and just use the ones they are interested in.

    For devices, this would mean a change in argument list and extra code to extract graphical parameters from the generic gpar structure. i.e., more work.

    The advantage would be that any future additions to the list of graphical parameters would not affect the API. Also, passing a single gpar ptr would be more efficient than the current practice of passing multiple graphical arguments.


Here's some concrete examples to show what the changes might look like to the user:
text(1, 1, "Works just like it does now", font=2)
text(1, 1, "Same effect as above", font=list(face="italic"))
text(1, 1, "Same effect as above", font=list(face=2))
text(1, 1, "Change the font just for this text",
     font=list(family="Helvetica", face="bold-italic"))
text(1:4, 1:4, paste("Font face", 1:4, "for this text"),
     font=list(family="Courier New", face=1:4))