3.5 Fonts and Font Sizes

If the most challenging aspects of Venndir were the intricacies of placing labels, surely the next big challenge was ensuring any chosen font rendered correctly.

Particularly for signed count labels, the default output uses Unicode up and down arrows: \(↑\) and \(↓\). These Unicode characters have caused the most heartache, for various reasons related to Unicode font support and computer platform locale.

Most* font rendering issues are handled smoothly by the combination of marquee, systemfonts, and textshaping. These R packages strive to "make it work" by finding each character glyph in the specified font, or by using a suitable font substitution.

Particularly for PDF output, it may be necessary to embed the font into the PDF file using grDevices::embedFonts() or grDevices::embedGlyphs(). Using cairo_pdf() should embed fonts automatically, but has rare exceptions.

Most fonts 'just work' thanks to R package marquee, with enough flexibility to keep the focus on effective and artistic choices, and away from any annoyances.

Figure 3.30: Most fonts 'just work' thanks to R package marquee, with enough flexibility to keep the focus on effective and artistic choices, and away from any annoyances.

3.5.1 Font Sizes

The default font size used in Venndir is 16 points, and is adjusted with the argument font_cex. Default font_cex=c(1, 1, 0.75) therefore defines values 16, 16, 12, which are applied in order as follows:

  • Set label
  • Count label
  • Signed count label(s)

The text is defined with a fixed font size, and is therefore independent of the size of the figure. If the figure itself is drawn on a small graphics device, the font sizes will remain the same, so some care should be taken to select font sizes appropriate for the output figure dimensions.

Ideally, the output device dimensions should be defined with fixed units, such as inches ('in'), or centimeters ('cm'), so that the final figure is clearly defined.

The argument font_cex has specific behavior based upon the number of values supplied in the argument:

  • font_cex with one value: It is multiplied by c(1, 1, 0.75) to be an easy adjustment to the typical defaults. Thus font_cex=2 will double all font sizes.
  • font_cex with two values: The second value is repeated to make a vector of three values. This vector is multiplied by c(1, 1, 0.75), again as convenient way to adjust Set and Count labels proportional to their default values.
    font_cex=c(2, 1) will double the Set label, keeping Count and Signed at their defaults.
  • font_cex with three values: Three values are used directly.

The adjusted font_cex is multiplied by 16 to define the font point size. Several examples are shown in Figure 3.31.

Venn diagram showing modifications to font sizes by adjusting the argument font_cex.Venn diagram showing modifications to font sizes by adjusting the argument font_cex.Venn diagram showing modifications to font sizes by adjusting the argument font_cex.Venn diagram showing modifications to font sizes by adjusting the argument font_cex.

Figure 3.31: Venn diagram showing modifications to font sizes by adjusting the argument font_cex.

The last example (bottom-right panel) was created using the following code:

v <- venndir(make_venn_test(do_signed=TRUE),
   font_cex=c(2, 1.3, 0.8))

3.5.2 Overall Font Family

The overall font typeface used by venndir() is defined with the argument fontfamily, for which the default is fontfamily="Arial".

See Available Font Families to review other fonts available.

Figure 3.32 uses the R 'serif' font, with fontfamily='serif'. .

setlist <- make_venn_test(n_sets=3, do_signed=TRUE)
v <- venndir(setlist,
   main="fontfamily='serif'",
   fontfamily="serif")
Venn diagram showing two sets, with all labels shown using a serif font, such as 'Times'.

Figure 3.32: Venn diagram showing two sets, with all labels shown using a serif font, such as 'Times'.

Note that all the labels use 'serif' now, including in the legend.

Similarly, for Venn diagrams which show item labels, the item font is adjusted with fontfamily. This type of plot can be created using venndir() with argument show_labels="Ni", to place set name 'N' outside, and item labels 'i' inside.

petlist <- list(
   Dog=c("Needs walks", "Barks", "Round eyes"),
   Cat=c("Free roam", "Meows", "Thin pupils"),
   "Dog&Cat"=c("Furry", "Has claws", "Lovable"))
petsetlist <- overlaplist2setlist(petlist)

petv <- venndir(petsetlist,
   font_cex=1.5,
   item_cex_factor=0.8,
   show_labels="Ni",
   draw_legend=FALSE,
   show_segment=FALSE,
   keep_item_order=TRUE,
   item_buffer=0, xyratio=2,
   fontfamily="Optima")
Venn diagram showing Two sets 'Dogs' and 'Cats' with some attributes unique to each, and shared by both. The item labels use the 'Optima' font.

Figure 3.33: Venn diagram showing Two sets 'Dogs' and 'Cats' with some attributes unique to each, and shared by both. The item labels use the 'Optima' font.

The example above includes some other useful customizations, discussed in detail later in the book. For now, here is a brief description:

  • font_cex=1.5 enlarges the set name font.
  • draw_legend=FALSE hides the color legend.
  • show_segment=FALSE hides the line segments.
  • keep_item_order=TRUE maintains the original order of item labels, therefore not sorting items alphabetically.
  • item_buffer=0 reduces the buffer zone of each Venn region before determining item label positions.
  • xyratio=2 increases the x:y ratio during item label placement, higher values tend to produce single column output.

Item labels can be displayed using venn_meme().

petlist <- list(
   Dog=c("Needs walks", "Barks", "Round eyes"),
   Cat=c("Free roam", "Meows", "Thin pupils"),
   "Dog&Cat"=c("Furry", "Has claws", "Lovable"))
venn_meme(petlist,
   font_cex=1.5,
   item_cex_factor=0.8,
   item_buffer=0, xyratio=1.5,
   fontfamily="Optima",
   show_labels="Ni")
Venn diagram showing Two sets 'Dogs' and 'Cats', this time created using venn_meme(). The item labels use the 'Optima' font.

Figure 3.34: Venn diagram showing Two sets 'Dogs' and 'Cats', this time created using venn_meme(). The item labels use the 'Optima' font.

3.5.3 Individual Font Families

For more control over specific fonts, use argument fontfamilies. This argument expects a list with these elements:

  • overlap - fontfamily to use for set or overlap labels
  • count - fontfamily to use for main count labels
  • signed - fontfamily to use for signed count labels

Any value not customized will use the default fontfamily value.

This example shows distinct fonts for each element, in fact it shows two fonts for "count" which corresponds to the main counts, and the percentage of counts, in order.

setlist <- make_venn_test(do_signed=TRUE)
venndir(setlist,
   show_labels="Ncps",
   main="Main Title (Times New Roman)",
   fontfamily="Times New Roman",
   y_inset=grid::unit(1, "lines"),
   expand_fraction=c(0.15, 0, 0, 0),
   fontfamilies=list(overlap="Optima",
      count=c("Arial Black", "Times"),
      signed="Arial Narrow"))
Venn diagram showing distinct font families used for each type of label: overlap, count, and signed.

Figure 3.35: Venn diagram showing distinct font families used for each type of label: overlap, count, and signed.

The arguments to assemble_venndir_label() also include fontfaces which may also be customized to control whether each font is 'plain', 'bold', 'italic', or 'bold.italic' for example. These customizations are rather advanced, and are discussed in more detail later.

3.5.4 Available Font Families

Before getting into details with fonts, some useful terminology should be defined. Thomas Pederson wrote an excellent overview of font terms specific to R in Typography and R, as part of the systemfonts (Pedersen, Ooms, and Govett 2025) package.

To paraphrase, the commonly used term "font" typically refers to a 'typeface', for example 'Arial' and 'Helvetica' are each typefaces. The typeface may have several variations that may include width (normal, bold, or thin), and style (normal, italic, or oblique). Each variation represents its own "font" within the typeface. Some typefaces such as 'Helvetica Neue' may have 18 or more individual fonts!

With these terms in mind, R refers to a typeface with either the term 'fontfamily' or 'family', and this convention is followed in Venndir.

The systemfonts R package (Pedersen, Ooms, and Govett 2025) is used by Venndir, by way of the marquee R package (Pedersen and Mitáš 2025).

A function subset_systemfonts() may be helpful to browse the available fonts on the system running R. The following example demonstrates how to summarize available fonts for 'Helvetica Neue', shown in Table 3.5.

subset_systemfonts(grepl("Helvetica Neue", family))
Table 3.5: Table 3.6: Partial summary of 'Helvetica Neue' fonts via systemfonts.
name family style weight
HelveticaNeue-UltraLight Helvetica Neue UltraLight thin
HelveticaNeue-Thin Helvetica Neue Thin ultralight
HelveticaNeue-Light Helvetica Neue Light light
HelveticaNeue Helvetica Neue Regular normal
HelveticaNeue-Medium Helvetica Neue Medium medium
HelveticaNeue-Bold Helvetica Neue Bold bold
HelveticaNeue-CondensedBold Helvetica Neue Condensed Bold bold
HelveticaNeue-CondensedBlack Helvetica Neue Condensed Black heavy
HelveticaNeue-UltraLightItalic Helvetica Neue UltraLight Italic thin
HelveticaNeue-ThinItalic Helvetica Neue Thin Italic ultralight
HelveticaNeue-LightItalic Helvetica Neue Light Italic light
HelveticaNeue-Italic Helvetica Neue Italic normal
HelveticaNeue-MediumItalic Helvetica Neue Medium Italic medium
HelveticaNeue-BoldItalic Helvetica Neue Bold Italic bold

Note that it uses grepl() on the column 'family' to subset the set of all available fonts. To list all fonts, use: subset_systemfonts().

3.5.4.1 Font family visualization

The results can be plotted by adding do_plot=TRUE, as a basic way of viewing the resulting fonts. Note that there are no provisions to prevent overlapping labels, so this approach is considered as a "quick check" to view only a few fonts.

ss <- subset_systemfonts(grepl("Helvetica", family), do_plot=TRUE)
Quick check of available fonts where the family name contains 'Helvetica'.

Figure 3.36: Quick check of available fonts where the family name contains 'Helvetica'.

3.5.4.2 Automatic font substitution

Another helpful feature of systemfonts is that it will substitute a font, or a glyph within a font, when it cannot otherwise be found. In Venndir, this approach helps ensure suitable Unicode characters are used by default for example.

For example, the font family 'sans' is replaced with the appropriate corresponding font, which is important because it differs on Windows, Mac, and linux architectures. The typical defaults, for example: 'Arial' on Windows; 'Helvetica' on MacOS; 'DejaVu Sans' on Linux.

Similarly, when trying to use 'Arial' on a machine which lacks 'Arial', systemfonts will substitute a suitable replacement, for example 'DejaVu Sans' or 'Helvetica'.

The font substitutions can be pre-defined, see systemfonts::font_fallback().

To review the font substitution, one can use systemfonts::font_info() in the simple example below, however there is much more capability described in the systemfonts documentation.

data.frame(systemfonts::font_info("sans")[, 1:9])
Table 3.7: Table 3.8: Output from the font info function in systemfonts showing the font substitution for 'sans'.
path index family style italic bold monospace weight width
/System/Library/Fonts/Helvetica.ttc 0 Helvetica Regular FALSE FALSE FALSE normal normal

References

Pedersen, Thomas Lin, and Martin Mitáš. 2025. Marquee: Markdown Parser and Renderer for r Graphics. https://marquee.r-lib.org.
Pedersen, Thomas Lin, Jeroen Ooms, and Devon Govett. 2025. Systemfonts: System Native Font Finding. https://github.com/r-lib/systemfonts.