Font Management in ksTFL

Overview

ksTFL renders DOCX documents using a C++ engine that requires TrueType/OpenType font files for text measurement and pagination. Starting with version 0.7.0, the package automatically discovers fonts installed on the operating system at load time. If a required font is not available on the system, a metrically compatible open-source fallback is used.

This guide explains how font discovery works, which fallback fonts are bundled, and how to configure custom font directories.

How Font Discovery Works

When ksTFL is loaded, it performs four steps:

  1. Scan system directories. The C++ font scanner inspects platform-specific directories and reads family names and style flags from .ttf, .otf, and .ttc files.

  2. Scan user directories. If the R option ksTFL.font_dirs is set, those directories are scanned as well.

  3. Scan bundled fallbacks. The package’s own inst/fonts/ directory is scanned last.

  4. Resolve target fonts. For each target family (Arial, Times New Roman, Courier New, Georgia, Verdana, Trebuchet MS), ksTFL keeps the discovered font when available or assigns a designated fallback.

Key rule: system-installed fonts always take priority. The bundled fallbacks are only used when a target font is not found anywhere on the system.

Platform-Specific Directories

The scanner inspects the following directories by default:

Platform Directories
Linux /usr/share/fonts, /usr/local/share/fonts, ~/.local/share/fonts, ~/.fonts, plus any XDG data dirs
macOS /System/Library/Fonts, /Library/Fonts, ~/Library/Fonts
Windows System fonts directory (from registry), plus user fonts in %LOCALAPPDATA%\Microsoft\Windows\Fonts

Target Fonts and Fallbacks

The rendering engine targets six font families commonly used in clinical documents. Each has a metrically compatible open-source fallback bundled with the package:

Target Font Fallback Font Notes
Arial Liberation Sans Metrically identical to Arial
Times New Roman Liberation Serif Metrically identical to Times New Roman
Courier New Liberation Mono Metrically identical to Courier New
Georgia Liberation Serif Serif fallback for Georgia
Verdana Liberation Sans Sans-serif fallback for Verdana
Trebuchet MS Liberation Sans Sans-serif fallback for Trebuchet MS

All bundled fonts are licensed under the SIL Open Font License 1.1.

If a font family requested in a spec (via s_font(font_name = "...")) is not a target font and is not found on the system, the engine falls back to Liberation Sans as the last resort.

For convenience, ksTFL also ships built-in style atoms for the target font families: font_arial, font_courier_new, font_times_new_roman, font_georgia, font_verdana, and font_trebuchet_ms. These atoms set only font_name, so they can be safely combined with size, colour, alignment, and other style atoms via f_combine().

Checking Font Status

After loading the package, call tfl_font_status() to see the current font resolution:

library(ksTFL)
tfl_font_status()
#> ksTFL font scan: 3 target(s) resolved, 3 using fallback
#>   [ok]       Arial                -> Arial
#>   [ok]       Times New Roman      -> Times New Roman
#>   [ok]       Courier New          -> Courier New
#>   [fallback] Georgia              -> Liberation Serif
#>   [fallback] Verdana              -> Liberation Sans
#>   [fallback] Trebuchet MS         -> Liberation Sans
#>   Scanned 5 directories

Reading the output:

  • [ok] — The target font was found on the system and will be used directly.
  • [fallback] — The target font was not found; the bundled fallback is used instead.

Adding Custom Font Directories

If proprietary fonts are installed in a non-standard location (e.g., a network share or a project-local folder), point the ksTFL.font_dirs option to those directories before loading the package, or rescan afterwards:

Option A: Set before loading

options(ksTFL.font_dirs = c("/opt/company-fonts", "/mnt/shared/fonts"))
library(ksTFL)

Option B: Set and rescan after loading

library(ksTFL)
options(ksTFL.font_dirs = "/opt/company-fonts")
tfl_rescan_fonts()

Both approaches produce the same result. The ksTFL.font_dirs option accepts a character vector of directory paths.

Rescanning Fonts

Call tfl_rescan_fonts() after:

  • Installing new system fonts
  • Changing the ksTFL.font_dirs option
  • Mounting a new network font directory
# Install Georgia to /usr/local/share/fonts/georgia/ ... then:
tfl_rescan_fonts()
#> ksTFL font scan: 6 target(s) resolved, 0 using fallback
#>   [ok]       Arial                -> Arial
#>   [ok]       Times New Roman      -> Times New Roman
#>   [ok]       Courier New          -> Courier New
#>   [ok]       Georgia              -> Georgia
#>   [ok]       Verdana              -> Verdana
#>   [ok]       Trebuchet MS         -> Trebuchet MS
#>   Scanned 5 directories

Startup Messages

When the package is attached, it prints a concise summary if any target fonts are using fallbacks:

ksTFL v0.7.0 - Clinical TFL Framework
For help, type: ??ksTFL
Note: 3 font(s) using fallback: Georgia, Verdana, Trebuchet MS
Run tfl_font_status() for details.

If all target fonts are found on the system, no font-related message is shown.

Interaction with write_doc()

The write_doc() function (and replay_report()) automatically uses the font directories from the scanner cache. You can still pass additional per-call directories via the font_dirs argument:

write_doc(report, name = "output",
          outDir = "results",
          metaPath = tempdir(),
          font_dirs = "/path/to/extra-fonts")

Per-call directories are appended to the scanner’s cached list for that render only — they do not modify the global font registry.

FAQ

Q: I see [fallback] for a font I know is installed. What’s wrong?

The font may be installed in a directory not scanned by default. Add its directory to ksTFL.font_dirs and rescan:

options(ksTFL.font_dirs = "/path/to/font/directory")
tfl_rescan_fonts()

Q: Can I use custom fonts not in the target list?

Yes. Any font found during scanning is available to the renderer. Use the family name as it appears in the font’s metadata (e.g., s_font(font_name = "Fira Sans")). If the font is in a non-standard directory, add that directory to ksTFL.font_dirs.

Q: Will documents look different on systems without the proprietary fonts?

The fallback fonts (Liberation family) are designed to be metrically compatible with their proprietary counterparts. Line breaks, page breaks, and column widths should be identical or very close. Minor glyph-level differences may exist, but document layout is preserved.

Q: How do I suppress the startup font warning?

You can suppress all startup messages with:

suppressPackageStartupMessages(library(ksTFL))

Or install the missing fonts and the message will not appear.