Paul Murrell
Department of Statistics
The University of Auckland
paul@stat.auckland.ac.nz
This document discusses how R allows fonts to be specified. This is within the context of R's graphics engine -- graphicssystems, such as base graphics and grid can obviously implementtheir own interfaces, but the engine capabilities will limit whatthey 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 differentpurposes (e.g., axis labels and titles).
One problem with this font specification is that the graphics enginedoes 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 atext file of different fonts, but this is not a terribly clean orconvenient mechanism.
Another problem is that there is no way to specify a change ininter-line spacing of text (for drawing a single piece of multi-linetext, 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 (normalor 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, whereface is a number basically like R's "font", with values higher than 4 indicating othernonstandard 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.
Proposal
What I want to do is add family and lineheight characteristicsto 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 eitherhave 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 bybase graphics for now. Even if added, it would just be an additionalpar() 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 onexisting user code.
The functions strwidth
and strheight
need to have arguments added to allow the specification of the fontfor 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.hHere are two options for doing this:
- Simply add additional lineheight and family arguments (and possiblyrename font argument to "face") to relevant graphical primitives (e.g., GEText and dev_text).
For devices this would only require modifying theargument list for a couple of functions and possibly renaming"font" to "face". i.e., treat the new face argument exactly likethe old font argument. Lineheight would only be used in the graphicsengine. Family could just be ignored until you feel like doing anythingabout it (although maybe could add a warning if given a non-empty family value).
- Change the way graphical arguments are passed to graphics engine andgraphics devices more substantially; create a new gpar structure(an SEXP?) within which all graphical arguments can be passed andreplace primitive-specific argument lists with a single gpar ptr argument. All graphics primitives get all graphical arguments andjust use the ones they are interested in.
For devices, this would meana 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 listof graphical parameters would not affect theAPI. Also, passing a single gpar ptr would be more efficient thanthe current practice of passing multiple graphical arguments.
Examples
Here's some concrete examples to show what the changes might look liketo 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))
Advantages
- There would be potential for devices to support changes in font family on-the-fly.
- Hershey vector fonts could be handled within the same mechanism as"normal" fonts. This could lead todeprecation of vfont arguments in text() et al, andno need for introduction of extra vfont arguments for grid.Could also lead to use of Hershey vector fonts in mathematicalannotation, which would allow mathematical annotation onALL devices.
Downsides
- Possibly some work for device maintainers (at least for gtkDevice).
- External packages which depend on new graphics API or device APIwould have to be recompiled and versions which run on 1.5.0 would not run on 1.6.0.This includes grid (=> lattice) and gtkDevice to my knowledge. On theother hand, this is already the case due to other changes.