Repository navigation
Releases: fschutt/rust-fontconfig
Release list
v5.0.1
[5.0.1] - 2026-10-06
Added
FcFontRegistry::wait_for_fonts: the waiting half ofrequest_fonts- loads the stacks' families and resolves no chain.
Fixed
request_and_resolve_with_scriptsno longer resolves (and memoizes) a default-script chain per call before the one asked for. A default-script chain holds every script group's fonts with their whole coverage: about 2 MB each with CJK fonts installed.
v5.0.0
[5.0.0] - 2026-09-03
Breaking
FontFallbackChain::unicode_fallbacksisVec<ScriptFallbackGroup>; newlast_resort;CssFallbackGroup::script_fonts.resolve_charreturnsNonewhen nothing covers the character; setFcFallbackConfig::last_resortfor a.notdeffont.queryranks style before coverage and never prefers wider fonts.FcPattern::unicode_rangesis exact cmap coverage; disk manifest v3 (a v2 manifest is rescanned).list()is registration order; identical patterns from different files are separate records.PatternMatchdiscriminants pinned to the C header (True=0,False=1,DontCare=2).scout_thread/builder_threadarepub(crate).
Added
FcFallbackConfig: generic families, substitutions, per-script preferences, last resort, default generic.os_defaults,empty,merge_defaults,absorb_system_aliases,candidate_families.GenericFamily(13 CSS generics:from_css,as_css,parent);FcScriptFallback.FcFontCache::{fallback_config, set_fallback_config, with_fallback_config};FcFontRegistry::new_with_configs.FcSystemConfig::{parse_tree, from_system}: fonts.conf tree, on every platform.FontFallbackChain::{empty, resolve_codepoint, fonts};fallback::RankKey.FcFontRegistry::set_persist_on_complete.utils::collect_font_files.- CI row
cache,async-registry,parsing; tag-triggered release workflow.
Changed
- Fallback chains are per script (#26): CSS tier (generics carry per-script preferred fonts) → per-block script tier → last resort. Ranked by coverage of the block, style, dedication, narrowness — never breadth.
resolve_char/query_for_textread only the chain: no lock, no clone per character.- Coverage comes from cmap segments (formats 4/12 exact); OS/2
ulUnicodeRangeis not consulted; block probes removed. - Registry prefetch is
candidate_families(stack, scripts)— what the chain can contain. FcFontRegistry::new()reads fonts.conf dirs and aliases.- Both scanners use one cycle-safe, extension-filtered walk.
- One record per font:
metadatais the store; dedup on insert;by_pathindex.
Fixed
- Relative fonts.conf
<include>resolved against the CWD; now$FONTCONFIG_PATH, then the config dir. Deterministic include order, cycle guard. build_completecould flip while parses were in flight (in_flightcounter).- Lost condvar wake-ups on
scan_complete/build_complete(published undercompleted_paths). - Lazy-mode builder threads leaked; threads hold a
Weaknow. - Fonts loaded from the manifest were missing from the family index.
- Duplicate registrations overwrote and orphaned ids.
FcFontRenderConfigEq/Ordconsistency (clippyderive_ord_xor_partial_ord).- OS/2 bit table was misaligned from bit 12 (table removed).
- Panics no longer unwind into C:
catch_unwindin every export;clippy --all-featurespasses. - Disk-cache tests are compiled out under
single-thread-unsafe-locks; autosave test is Unix-only. has_*_rangestest overlap with the block.
Removed
- Token-fuzzy path (
fuzzy_query_by_name, token index),find_unicode_fallbacks,calculate_font_similarity_score,query_internal*,system_aliasesstate, OS/2 range table, cmap block probes, web-lift last-resort branches, the pattern-keyed map. - Workflows
c-bindings.ymlandrust.yml(duplicates ofci.yml).
Deprecated
OperatingSystem::{get_serif_fonts, get_sans_serif_fonts, get_monospace_fonts, expand_generic_family},expand_font_families,FcFontCache::{expand_font_families_config_first, system_alias_prefs, resolve_font_chain_with_os}— thin wrappers overFcFallbackConfig::os_defaults.
Upgrade notes
- Inject the tables:
FcFontRegistry::new_with_configs(FcScanConfig::os_defaults(os), FcFallbackConfig::os_defaults(os))orcache.set_fallback_config(..). - A font for uncovered characters:
FcFallbackConfig::last_resort. - First run after upgrading rescans (manifest v3).
Tests
tests/issue_26_unicode_fallback_ranking.rs,tests/registry_completion.rs, fonts.conf tree, coverage exactness, walk cycle, header parity, one-record-per-font.
v4.4.4
In-memory fonts registered via with_memory_fonts are now usable on system-font-less caches: cmap is parsed into unicode_ranges (parsing feature), and generic-family resolution falls back to registered fonts when OS-list expansion matches nothing. Fixes the azul web/wasm fallback font.
rust-fontconfig 3.0.0
Breaking Changes
FcPatternhas a new field:render_config: FcFontRenderConfigwas added. Code
using struct literal construction (FcPattern { name: ..., family: ... }) must add
render_config: FcFontRenderConfig::default()or use..Default::default().UnicodeRangeis now#[repr(C)]: Layout is guaranteed to match C{ uint32_t start; uint32_t end; }.
This was already the case in practice but is now an explicit contract.ffifeature now impliesasync-registry: The C bindings include the full
registry (background thread) API. Previouslyffionly impliedparsing+std.
New Features
Per-font rendering config from fonts.conf (#16)
On Linux, fonts.conf <match target="font"> rules are now parsed and exposed via
FcFontRenderConfig on each FcPattern. Supported properties:
| Field | Type | Description |
|---|---|---|
antialias |
Option<bool> |
Enable/disable antialiasing |
hinting |
Option<bool> |
Enable/disable hinting |
hintstyle |
Option<FcHintStyle> |
None, Slight, Medium, Full |
autohint |
Option<bool> |
Use autohinter |
rgba |
Option<FcRgba> |
Subpixel order (Rgb, Bgr, Vrgb, Vbgr) |
lcdfilter |
Option<FcLcdFilter> |
LCD filter mode |
embeddedbitmap |
Option<bool> |
Use embedded bitmaps |
embolden |
Option<bool> |
Synthetic bold |
dpi |
Option<f64> |
Per-font DPI override |
scale |
Option<f64> |
Scale factor |
minspace |
Option<bool> |
Minimum spacing |
All fields are None on macOS/Windows (use system defaults). New enums:
FcHintStyle, FcRgba, FcLcdFilter.
Async font registry C API
12 new C FFI functions expose the background-thread font loading API:
// Lifecycle
FcFontRegistry fc_registry_new(void);
void fc_registry_spawn(FcFontRegistry registry);
void fc_registry_shutdown(FcFontRegistry registry);
void fc_registry_free(FcFontRegistry registry);
// Priority-based font loading (blocks only for requested fonts)
FcFontChain* fc_registry_request_fonts(registry, stacks, counts, num, &out);
void fc_registry_chains_free(FcFontChain* chains, size_t count);
// Status
bool fc_registry_is_scan_complete(FcFontRegistry registry);
bool fc_registry_is_build_complete(FcFontRegistry registry);
// Query
FcFontMatch* fc_registry_query(FcFontRegistry registry, const FcPattern* pattern);
FcFontInfo* fc_registry_list_fonts(FcFontRegistry registry, size_t* count);
FcFontChain fc_registry_resolve_font_chain(registry, families, count, weight, italic, oblique);
FcFontPath* fc_registry_get_font_path(FcFontRegistry registry, const FcFontId* id);
FcFontMetadata* fc_registry_get_metadata(FcFontRegistry registry, const FcFontId* id);
FcFontRenderConfig fc_registry_get_render_config(FcFontRegistry registry, const FcFontId* id);
FcFontCache fc_registry_snapshot(FcFontRegistry registry);Unicode script detection helpers
New public functions for checking Unicode block coverage:
pub fn has_cjk_ranges(ranges: &[UnicodeRange]) -> bool;
pub fn has_arabic_ranges(ranges: &[UnicodeRange]) -> bool;
pub fn has_cyrillic_ranges(ranges: &[UnicodeRange]) -> bool;
pub fn has_hebrew_ranges(ranges: &[UnicodeRange]) -> bool;
pub fn has_thai_ranges(ranges: &[UnicodeRange]) -> bool;New public modules
Internal code has been restructured into public modules for better organization:
config-- OS-specific font directories, generic family expansion, tokenizationscoring-- Priority queue types and scoring heuristics for background loadingmultithread-- Scout and builder thread implementationsdisk_cache-- Disk cache serialization (behindcachefeature)utils-- Font file detection, family name normalization
Improvements
- 14% code reduction (8018 -> 6905 lines) through systematic deduplication
- FFI safety: Fixed
Vec::from_raw_partscapacity mismatch UB infc_registry_request_fonts - Code style: Flattened deeply nested
if letpatterns withlet-elseand functional chaining - CI: Added cross-compilation checks for WASM, iOS, iOS Simulator, Android targets;
fixed Windows MSVC example execution; removedcontinue-on-errorthat hid failures
C Example
New ffi/example_registry.c with 3 demos showcasing the azul-style fast startup pattern:
- Azul-style fast startup: spawn threads -> request only needed fonts -> render first frame
with 67/806 fonts loaded, remaining parse in background - Incremental loading: request UI fonts (69 loaded) -> monospace for code editor (90) ->
CJK for pasted Japanese text (116) - Old vs new API comparison: blocking
fc_cache_build()(806 fonts) vs async
fc_registry_request_fonts()(67 fonts, rest in background)
Migration from 2.0.0
// Before (2.0.0)
let pattern = FcPattern {
name: Some("Arial".into()),
family: Some("Arial".into()),
..Default::default()
};
// After (3.0.0) -- add render_config or use ..Default::default()
let pattern = FcPattern {
name: Some("Arial".into()),
family: Some("Arial".into()),
..Default::default() // render_config defaults to all-None
};If you were constructing FcPattern with all fields explicitly, add:
render_config: FcFontRenderConfig::default(),1.2.0
Breaking Changes
-
resolve_font_chain()signature changed: Thetextparameter has been removed. Font chains are now resolved based on CSS properties only (font-family, weight, italic, oblique), not text content.- cache.resolve_font_chain(&families, text, weight, italic, oblique, &mut trace); + cache.resolve_font_chain(&families, weight, italic, oblique, &mut trace);
-
query_all()method removed: Usecache.list()with filtering instead.- let fonts = cache.query_all(&pattern, &mut trace); + let fonts: Vec<_> = cache.list().into_iter() + .filter(|(pattern, _id)| /* your filter */) + .collect();
-
query_for_text()moved toFontFallbackChain: Text-to-font resolution now requires a font chain first.- let fonts = cache.query_for_text(&pattern, text, &mut trace); + let chain = cache.resolve_font_chain(&families, weight, italic, oblique, &mut trace); + let font_runs = chain.query_for_text(&cache, text);
Added
-
FontFallbackChain::resolve_text(): Returns per-character font assignments asVec<(char, Option<(FontId, String)>)>for fine-grained control. -
FontFallbackChain::resolve_char(): Resolve a single character to its font using the font chain. -
CssFallbackGroupstruct: Groups fonts by their CSS source name, making it clear which CSS font-family each font came from. -
Font chain caching: Identical CSS font-family stacks now share cached font chains, improving performance when the same fonts are used with different text content.
Changed
-
Architecture: The new two-step workflow (chain resolution → text querying) better matches CSS/browser font handling semantics and enables better caching.
-
Performance: Font chains are now cached by CSS properties, avoiding redundant font resolution for the same font-family declarations.
Links
- Documentation: https://docs.rs/rust-fontconfig
- Crate: https://crates.io/crates/rust-fontconfig
- Repository: https://github.com/fschutt/rust-fontconfig
1.0.0
rust-fontconfig v1.0.0 Release Notes
Overview
rust-fontconfig is a pure-Rust alternative to the Linux fontconfig library with no system dependencies. It supports .woff, .woff2, .ttc, .otf, and .ttf formats and works on Windows, macOS, and WASM environments.
Key Features
- Zero external dependencies - uses Rust's native libraries only
- Cross-platform support (Linux, Windows, macOS, and WASM)
- Memory-safe font parsing via allsorts (reduces risk of font-based attacks)
- Multithreaded font loading and parsing
- In-memory font caching for improved performance
- Flexible font matching by name, family, style, and Unicode ranges
- Automatic fallback selection for multilingual text
- C API for integration with non-Rust languages
Improvements Over C Implementation
- Smaller codebase than original fontconfig (~190,000 lines of C)
- Multithreaded parsing for faster initialization
- Memory-mapping for efficient file access
- Selective table parsing (only reads tables needed for matching)
- Lower memory consumption due to fewer allocations
- In-memory caching (vs disk-only in original implementation)
Usage (Rust)
use rust_fontconfig::{FcFontCache, FcPattern};
fn main() {
let cache = FcFontCache::build();
// Simple query
let results = cache.query(
&FcPattern {
name: Some(String::from("Arial")),
..Default::default()
},
&mut Vec::new()
);
if let Some(font_match) = results {
println!("Font match ID: {:?}", font_match.id);
}
// find all monospace fonts
let fonts = cache.query_all(
&FcPattern {
monospace: PatternMatch::True,
..Default::default()
},
&mut Vec::new()
);
println!("Found {} monospace fonts", fonts.len());
// Multilingual text support with fallback fonts
let matched_fonts = cache.query_for_text(
&FcPattern::default(),
"Hello 你好 Здравствуйте",
&mut Vec::new()
);
println!("You need {} fonts to render this text", matched_fonts.len());
}Usage (C)
- Put
rust_fontconfig.hin the same directory andlibrust_fontconfig.soin the same directory - Compile with
gcc -I. -L. -lrust_fontconfig example.c -o exampleclang -I. -L. -lrust_fontconfig example.c -o example
./exampleto run
#include <stdio.h>
#include "rust_fontconfig.h"
int main() {
// Build the font cache
FcFontCache cache = fc_cache_build();
if (!cache) {
fprintf(stderr, "Failed to build font cache\n");
return 1;
}
// Create a pattern to search for Arial
FcPattern* pattern = fc_pattern_new();
fc_pattern_set_name(pattern, "Arial");
// Search for the font
FcTraceMsg* trace = NULL;
size_t trace_count = 0;
FcFontMatch* match = fc_cache_query(cache, pattern, &trace, &trace_count);
if (match) {
char id_str[40];
fc_font_id_to_string(&match->id, id_str, sizeof(id_str));
printf("Found font! ID: %s\n", id_str);
// Get the font path
FcFontPath* font_path = fc_cache_get_font_path(cache, &match->id);
if (font_path) {
printf("Font path: %s (index: %zu)\n", font_path->path, font_path->font_index);
fc_font_path_free(font_path);
}
fc_font_match_free(match);
} else {
printf("Font not found\n");
}
// Clean up
fc_pattern_free(pattern);
if (trace) fc_trace_free(trace, trace_count);
fc_cache_free(cache);
return 0;
}Links
- Documentation: https://docs.rs/rust-fontconfig
- Crate: https://crates.io/crates/rust-fontconfig
- Repository: https://github.com/fschutt/rust-fontconfig