Font Management in ksTFL
ksTFL Development Team
2026-10-03
Source:vignettes/Font_Management.Rmd
Font_Management.RmdOverview
ksTFL renders DOCX documents using a C++ engine that requires TrueType/OpenType font files for text measurement and pagination. 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:
Scan system directories. The C++ font scanner walks the platform-specific directories recursively and reads family names and style flags from
.ttf,.otf, and.ttcfiles. For a given family and style the first file found wins, which is why earlier-scanned directories take priority.Scan user directories. If the R option
ksTFL.font_dirsis set, those directories are scanned as well.Scan bundled fallbacks. The package’s own
inst/fonts/directory is scanned last.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 the
fonts/ subdirectory of each $XDG_DATA_DIRS
entry |
| macOS |
/System/Library/Fonts,
/System/Library/Fonts/Supplemental,
/Library/Fonts, ~/Library/Fonts
|
| Windows | System fonts directory (from the OS API; typically
C:\Windows\Fonts), 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.
The R layer accepts only these seven families in
s_font(font_name = ...): the six targets above plus
Liberation Sans. If the renderer encounters a family it
cannot resolve on disk (possible only in a hand-edited spec or template
JSON), it falls back to the bundled Liberation Sans.
For convenience, ksTFL also registers built-in style names for the
target families: font_arial, font_courier_new,
font_times_new_roman, font_georgia,
font_verdana, and font_trebuchet_ms. These are
strings you pass to style slots (labelStyleRef,
valueStyleRef, styleRef) or to
f_combine(); each sets only font_name, so they
combine safely with size, colour, alignment, and other style atoms.
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 directoriesThe counts depend on which proprietary fonts are installed and how many font directories exist on your machine; the report always lists all six targets with their resolution.
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. -
(not found)— Neither the family nor its fallback resolved on disk (rare; usually a broken or unreadable font file).
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
This works only when the option is set before ksTFL is first loaded in the session; if the package is already loaded, use Option B.
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_dirsoption - Mounting a new network font directory
# Install Georgia to /usr/local/share/fonts/georgia/ ... then:
tfl_rescan_fonts()
#> ksTFL font scan: 1 target(s) resolved, 5 using fallback
#> [fallback] Arial -> Liberation Sans
#> [fallback] Times New Roman -> Liberation Serif
#> [fallback] Courier New -> Liberation Mono
#> [ok] Georgia -> Georgia
#> [fallback] Verdana -> Liberation Sans
#> [fallback] Trebuchet MS -> Liberation Sans
#> Scanned 6 directoriesA machine shows all six [ok] only when every proprietary
family is actually installed (Windows workstations typically are; bare
Linux servers typically are not).
Startup Messages
When the package is attached, it prints a concise summary if any target fonts are using fallbacks:
ksTFL v0.12.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()
write_doc() uses the font directories from the scanner
cache. You can pass additional per-call directories via its
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.
replay_report() uses the same cache but has no
font_dirs argument; register extra directories with
options(ksTFL.font_dirs = ...) plus
tfl_rescan_fonts() before replaying.
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?
Not in s_font(). The R layer accepts exactly seven
families: Arial, Courier New, Times New Roman, Georgia, Verdana,
Trebuchet MS, and Liberation Sans. Fonts the scanner discovers outside
this set are visible to the internal resolver but cannot be named in a
spec — pick the closest allowed family. The scanning directories still
matter for the six target families: if one of them lives in a
non-standard directory, add that directory to
ksTFL.font_dirs so the resolver finds the real font instead
of the fallback.
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.