Skip to content

Releases: fschutt/rust-fontconfig

v5.0.1

Choose a tag to compare

@github-actions github-actions released this 06 Oct 16:51

[5.0.1] - 2026-10-06

Added

  • FcFontRegistry::wait_for_fonts: the waiting half of request_fonts - loads the stacks' families and resolves no chain.

Fixed

  • request_and_resolve_with_scripts no 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

Choose a tag to compare

@github-actions github-actions released this 03 Sep 11:26

[5.0.0] - 2026-09-03

Breaking

  • FontFallbackChain::unicode_fallbacks is Vec<ScriptFallbackGroup>; new last_resort; CssFallbackGroup::script_fonts.
  • resolve_char returns None when nothing covers the character; set FcFallbackConfig::last_resort for a .notdef font.
  • query ranks style before coverage and never prefers wider fonts.
  • FcPattern::unicode_ranges is exact cmap coverage; disk manifest v3 (a v2 manifest is rescanned).
  • list() is registration order; identical patterns from different files are separate records.
  • PatternMatch discriminants pinned to the C header (True=0, False=1, DontCare=2).
  • scout_thread/builder_thread are pub(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_text read only the chain: no lock, no clone per character.
  • Coverage comes from cmap segments (formats 4/12 exact); OS/2 ulUnicodeRange is 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: metadata is the store; dedup on insert; by_path index.

Fixed

  • Relative fonts.conf <include> resolved against the CWD; now $FONTCONFIG_PATH, then the config dir. Deterministic include order, cycle guard.
  • build_complete could flip while parses were in flight (in_flight counter).
  • Lost condvar wake-ups on scan_complete/build_complete (published under completed_paths).
  • Lazy-mode builder threads leaked; threads hold a Weak now.
  • Fonts loaded from the manifest were missing from the family index.
  • Duplicate registrations overwrote and orphaned ids.
  • FcFontRenderConfig Eq/Ord consistency (clippy derive_ord_xor_partial_ord).
  • OS/2 bit table was misaligned from bit 12 (table removed).
  • Panics no longer unwind into C: catch_unwind in every export; clippy --all-features passes.
  • Disk-cache tests are compiled out under single-thread-unsafe-locks; autosave test is Unix-only.
  • has_*_ranges test overlap with the block.

Removed

  • Token-fuzzy path (fuzzy_query_by_name, token index), find_unicode_fallbacks, calculate_font_similarity_score, query_internal*, system_aliases state, OS/2 range table, cmap block probes, web-lift last-resort branches, the pattern-keyed map.
  • Workflows c-bindings.yml and rust.yml (duplicates of ci.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 over FcFallbackConfig::os_defaults.

Upgrade notes

  • Inject the tables: FcFontRegistry::new_with_configs(FcScanConfig::os_defaults(os), FcFallbackConfig::os_defaults(os)) or cache.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

Choose a tag to compare

@fschutt fschutt released this 11 Jun 08:46

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

Choose a tag to compare

@fschutt fschutt released this 02 Apr 21:28

Breaking Changes

  • FcPattern has a new field: render_config: FcFontRenderConfig was added. Code
    using struct literal construction (FcPattern { name: ..., family: ... }) must add
    render_config: FcFontRenderConfig::default() or use ..Default::default().
  • UnicodeRange is 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.
  • ffi feature now implies async-registry: The C bindings include the full
    registry (background thread) API. Previously ffi only implied parsing + 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, tokenization
  • scoring -- Priority queue types and scoring heuristics for background loading
  • multithread -- Scout and builder thread implementations
  • disk_cache -- Disk cache serialization (behind cache feature)
  • utils -- Font file detection, family name normalization

Improvements

  • 14% code reduction (8018 -> 6905 lines) through systematic deduplication
  • FFI safety: Fixed Vec::from_raw_parts capacity mismatch UB in fc_registry_request_fonts
  • Code style: Flattened deeply nested if let patterns with let-else and functional chaining
  • CI: Added cross-compilation checks for WASM, iOS, iOS Simulator, Android targets;
    fixed Windows MSVC example execution; removed continue-on-error that hid failures

C Example

New ffi/example_registry.c with 3 demos showcasing the azul-style fast startup pattern:

  1. Azul-style fast startup: spawn threads -> request only needed fonts -> render first frame
    with 67/806 fonts loaded, remaining parse in background
  2. Incremental loading: request UI fonts (69 loaded) -> monospace for code editor (90) ->
    CJK for pasted Japanese text (116)
  3. 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

Choose a tag to compare

@fschutt fschutt released this 25 Nov 23:02

Breaking Changes

  • resolve_font_chain() signature changed: The text parameter 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: Use cache.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 to FontFallbackChain: 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 as Vec<(char, Option<(FontId, String)>)> for fine-grained control.

  • FontFallbackChain::resolve_char(): Resolve a single character to its font using the font chain.

  • CssFallbackGroup struct: 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

1.0.0

Choose a tag to compare

@fschutt fschutt released this 13 Mar 22:07

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)

  1. Put rust_fontconfig.h in the same directory and librust_fontconfig.so in the same directory
  2. Compile with
    • gcc -I. -L. -lrust_fontconfig example.c -o example
    • clang -I. -L. -lrust_fontconfig example.c -o example
  3. ./example to 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