slint-ui/slint · critical
fatal: swash is unable to parse truetype font
Error message
fatal: swash is unable to parse truetype font
What it means
In the software renderer's system-font backend, fonts are enumerated by fontique and then converted to swash::FontRef to obtain shaping/cache keys. get_swash_font_info assumes every blob fontique hands over is a well-formed TrueType/OpenType file; if swash::FontRef::from_index cannot parse the data at the given collection index, the code panics with 'fatal: swash is unable to parse truetype font'. The panic happens the first time such a font is needed for shaping, deep inside text layout.
Source
Thrown at internal/renderers/software/fonts/systemfonts.rs:30
use super::super::PhysicalLength;
use super::vectorfont::VectorFont;
struct CachedFontInfo {
swash_key: swash::CacheKey,
swash_offset: u32,
}
i_slint_core::thread_local! {
// swash font info cached and indexed by fontique blob id (unique incremental) and true type collection index
static SWASH_FONTS: RefCell<HashMap<(HashedBlob, u32), CachedFontInfo>> = Default::default();
}
pub fn get_swash_font_info(blob: &fontique::Blob<u8>, index: u32) -> (swash::CacheKey, u32) {
SWASH_FONTS.with(|font_cache| {
let mut cache = font_cache.borrow_mut();
let info = cache.entry((blob.clone().into(), index)).or_insert_with(move || {
let font_ref = swash::FontRef::from_index(blob.data(), index as usize)
.expect("fatal: swash is unable to parse truetype font");
CachedFontInfo { swash_key: font_ref.key, swash_offset: font_ref.offset }
});
(info.swash_key, info.swash_offset)
})
}
fn get_swash_font_info_for_query_font(font: &fontique::QueryFont) -> (swash::CacheKey, u32) {
get_swash_font_info(&font.blob, font.index)
}
pub fn match_font(
request: &super::FontRequest,
scale_factor: super::ScaleFactor,
collection: &mut fontique::Collection,
source_cache: &mut fontique::SourceCache,
) -> Option<VectorFont> {
if request.family.is_some() {
let requested_pixel_size: PhysicalLength =View on GitHub (pinned to 3fd8f2ec03)
Solutions
- Find and remove/reinstall the broken font: parse every installed font with fontTools (or fc-scan) and act on the one that fails.
- Rebuild the font cache afterwards: fc-cache -r --force.
- If the file is app-managed, re-export or re-download the font (e.g. fontTools ttx round-trip) and re-ship it.
- As a temporary workaround while fixing the font, switch to a renderer that uses a different font stack (renderer-femtovg or renderer-skia).
Example fix
# before: panic 'fatal: swash is unable to parse truetype font' on first text draw
python3 - <<'EOF'
# after: identify the exact broken font, then remove or reinstall it
from fontTools.ttLib import TTFont, TTLibError
import pathlib
for p in pathlib.Path('/usr/share/fonts').rglob('*'):
if p.suffix.lower() in {'.ttf', '.otf', '.ttc'}:
try:
TTFont(str(p))
except (TTLibError, Exception) as e:
print('BROKEN FONT - remove or reinstall:', p, e)
EOF Defensive patterns
Strategy: validation
Validate before calling
# Validate every font the app will enumerate BEFORE first text draw:
from fontTools.ttLib import TTFont
import pathlib
def fonts_ok() -> bool:
for p in pathlib.Path('/usr/share/fonts').rglob('*'):
if p.suffix.lower() in {'.ttf', '.otf', '.ttc'}:
try:
TTFont(str(p))
except Exception:
return False
return True
assert fonts_ok(), 'unparseable system font present - software renderer will panic' Try / catch
// The panic is inside text layout; contain it only to keep a headless
// service alive, then fix the font set and restart:
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
slint::run_event_loop_until_quit()
}));
if r.is_err() {
// dump installed fonts, remove the unparseable one, restart
} Prevention
- Validate shipped/system fonts with fontTools or fc-scan in image-build CI
- Do not replace font files while the app runs (fontique caches blobs by id)
- Run fc-cache -r --force after font updates
- Prefer embedding one known-good font over relying on system enumeration where possible
When it happens
Trigger: Rendering any text with the software renderer (renderer-software / SLINT_BACKEND with software, or embedded targets using systemfonts) while at least one installed system font is corrupt, truncated, zero-byte, or is a collection whose entry at the reported index cannot be parsed by swash.
Common situations: A partially downloaded or disk-corrupted font in ~/.fonts or /usr/share/fonts; container/OS images shipping a malformed .ttc; stale fontconfig cache pointing at a replaced file; exotic or quasi-OpenType fonts that one parser accepts and swash rejects.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Polling completed or aborted JoinHandle
- SystemTrayIcon must be created on the main thread on macOS
- unable to show popup window
AI-assisted analysis of slint-ui/slint@3fd8f2ec03 (2026-08-19).
Data as JSON: /api/errors/0138368d8246d3f9.
Report an issue: GitHub.