{"id":20400323,"url":"https://github.com/fschutt/rust-fontconfig","last_synced_at":"2026-04-03T00:43:01.235Z","repository":{"id":46348749,"uuid":"343079528","full_name":"fschutt/rust-fontconfig","owner":"fschutt","description":"Pure-Rust rewrite of the Linux fontconfig library (no system dependencies) - using ttf-parser and allsorts","archived":false,"fork":false,"pushed_at":"2025-03-13T21:53:19.000Z","size":126,"stargazers_count":73,"open_issues_count":2,"forks_count":12,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T10:08:13.490Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fschutt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-28T10:43:53.000Z","updated_at":"2025-03-27T02:56:29.000Z","dependencies_parsed_at":"2023-12-28T02:28:29.547Z","dependency_job_id":"eed66031-489e-454b-914d-9b535c150be5","html_url":"https://github.com/fschutt/rust-fontconfig","commit_stats":{"total_commits":23,"total_committers":3,"mean_commits":7.666666666666667,"dds":"0.17391304347826086","last_synced_commit":"996c8ac0ea33fee5db9863c6c735627bfaaaba1e"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Frust-fontconfig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Frust-fontconfig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Frust-fontconfig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Frust-fontconfig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fschutt","download_url":"https://codeload.github.com/fschutt/rust-fontconfig/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247325694,"owners_count":20920714,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T04:39:33.873Z","updated_at":"2026-04-03T00:43:01.230Z","avatar_url":"https://github.com/fschutt.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rust-fontconfig\n\nPure-Rust rewrite of the Linux fontconfig library (no system dependencies) - using allsorts as a font parser to support `.woff`, `.woff2`, `.ttc`, `.otf` and `.ttf`\n\n**NOTE**: Also works on Windows, macOS and WASM - without external dependencies!\n\n## Motivation\n\nThere are a number of reasons why I want to have a pure-Rust version of fontconfig:\n\n- fontconfig with all dependencies (expat and freetype) is ~190.000 lines of C (extremely bloated for what it does)\n- fontconfig, freetype, expat and basically any kind of parsing in C is a common attack vector (via maliciously crafted fonts). The Rust version (allsorts) checks the boundaries before accessing memory, so attacks via font files should be less common.\n- it gets rid of the cmake / cc dependencies necessary to build [azul](https://azul.rs) on Linux\n- fontconfig isn't really a \"hard\" library to rewrite, it just parses fonts and selects fonts by name\n- Rust has existing xml parsers and font parsers, just use those\n- It allows fontconfig libraries to be purely statically linked\n- Font parsing / loading can be easily multithreaded (parsing font files in parallel)\n- It reduces the number of necessary non-Rust dependencies on Linux for azul to 0\n- fontconfig (or at least the Rust bindings) do not allow you to store an in-memory cache, only an on-disk cache, requiring disk access on every query (= slow)\n- `no_std` support (\"bring your own font files\") for WASM\n \nNow for the more practical reasons:\n\n- libfontconfig 0.12.x sometimes hangs and crashes ([see issue](https://github.com/maps4print/azul/issues/110))\n- libfontconfig introduces build issues with cmake / cc ([see issue](https://github.com/maps4print/azul/issues/206))\n- To support font fallback in CSS selectors and text runs based on Unicode ranges, you have to do several calls into C, since fontconfig doesn't handle that\n- The rust rewrite uses multithreading and memory mapping, since that is faster than reading each file individually\n- The rust rewrite only parses the font tables necessary to select the name, not the entire font\n- The rust rewrite uses very few allocations (some are necessary because of UTF-16 / UTF-8 conversions and multithreading lifetime issues)\n\n## Usage\n\n### Basic Font Query\n\n```rust\nuse rust_fontconfig::{FcFontCache, FcPattern};\n\nfn main() {\n    // Build the font cache (scans system fonts)\n    let cache = FcFontCache::build();\n    \n    // Query a font by name\n    let mut trace = Vec::new();\n    let results = cache.query(\n        \u0026FcPattern {\n            name: Some(String::from(\"Arial\")),\n            ..Default::default()\n        },\n        \u0026mut trace\n    );\n    \n    if let Some(font_match) = results {\n        println!(\"Font match ID: {:?}\", font_match.id);\n        println!(\"Font unicode ranges: {:?}\", font_match.unicode_ranges);\n        \n        // Get font metadata\n        if let Some(meta) = cache.get_metadata_by_id(\u0026font_match.id) {\n            println!(\"Family: {:?}\", meta.family);\n        }\n        \n        // Get font file path\n        if let Some(source) = cache.get_font_by_id(\u0026font_match.id) {\n            match source {\n                rust_fontconfig::FontSource::Disk(path) =\u003e {\n                    println!(\"Path: {}\", path.path);\n                }\n                rust_fontconfig::FontSource::Memory(font) =\u003e {\n                    println!(\"Memory font: {}\", font.id);\n                }\n            }\n        }\n    } else {\n        println!(\"No matching font found\");\n    }\n}\n```\n\n### Font Fallback Chain for CSS font-family\n\nThe new API separates font chain resolution from text querying:\n\n1. **`resolve_font_chain()`** - Create a fallback chain from CSS font-family (without text)\n2. **`chain.resolve_text()`** - Query which fonts to use for specific text\n\n```rust\nuse rust_fontconfig::{FcFontCache, FcWeight, PatternMatch};\n\nfn main() {\n    let cache = FcFontCache::build();\n    \n    // Step 1: Build font fallback chain (without text parameter)\n    let mut trace = Vec::new();\n    let font_chain = cache.resolve_font_chain(\n        \u0026[\"Arial\".to_string(), \"sans-serif\".to_string()],\n        FcWeight::Normal,\n        PatternMatch::DontCare,  // italic\n        PatternMatch::DontCare,  // oblique\n        \u0026mut trace,\n    );\n    \n    println!(\"CSS fallback groups: {}\", font_chain.css_fallbacks.len());\n    for group in \u0026font_chain.css_fallbacks {\n        println!(\"  CSS '{}' resolved to {} fonts\", group.css_name, group.fonts.len());\n    }\n    \n    // Step 2: Query which fonts to use for specific text\n    let text = \"Hello 你好 Здравствуйте\";\n    let font_runs = font_chain.query_for_text(\u0026cache, text);\n    \n    println!(\"\\nText '{}' split into {} font runs:\", text, font_runs.len());\n    for run in \u0026font_runs {\n        println!(\"  '{}' -\u003e font {:?}\", run.text, run.font_id);\n    }\n}\n```\n\n### Character-by-Character Font Resolution\n\nFor fine-grained control, use `resolve_text()` to get per-character font assignments:\n\n```rust\nuse rust_fontconfig::{FcFontCache, FcWeight, PatternMatch};\n\nfn main() {\n    let cache = FcFontCache::build();\n    \n    let chain = cache.resolve_font_chain(\n        \u0026[\"sans-serif\".to_string()],\n        FcWeight::Normal,\n        PatternMatch::False,\n        PatternMatch::False,\n        \u0026mut Vec::new(),\n    );\n    \n    // Get font assignment for each character\n    let text = \"Hello 世界\";\n    let resolved = chain.resolve_text(\u0026cache, text);\n    \n    for (ch, font_info) in resolved {\n        match font_info {\n            Some((font_id, css_source)) =\u003e {\n                let font_name = cache.get_metadata_by_id(\u0026font_id)\n                    .and_then(|m| m.name.clone().or(m.family.clone()))\n                    .unwrap_or_default();\n                println!(\"'{}' -\u003e {} (from CSS '{}')\", ch, font_name, css_source);\n            }\n            None =\u003e println!(\"'{}' -\u003e NO FONT FOUND\", ch),\n        }\n    }\n}\n```\n\n### List All Fonts Matching a Pattern\n\n```rust\nuse rust_fontconfig::{FcFontCache, FcWeight};\n\nfn main() {\n    let cache = FcFontCache::build();\n    \n    // List all fonts - filter by properties\n    let bold_fonts: Vec\u003c_\u003e = cache.list().into_iter()\n        .filter(|(pattern, _id)| {\n            matches!(pattern.weight, FcWeight::Bold | FcWeight::ExtraBold)\n        })\n        .collect();\n\n    println!(\"Found {} bold fonts:\", bold_fonts.len());\n    for (pattern, id) in bold_fonts.iter().take(5) {\n        println!(\"  {:?}: {:?}\", id, pattern.name.as_ref().or(pattern.family.as_ref()));\n    }\n}\n```\n\n## Using from C\n\n### Linking with the C API\n\nThe rust-fontconfig library provides C-compatible bindings that can be used from C/C++ applications.\n\n#### Binary Downloads\n\nYou can download pre-built binary files from the [latest GitHub release](https://github.com/maps4print/rust-fontconfig/releases/latest):\n- Windows: `rust_fontconfig.dll` and `rust_fontconfig.lib`\n- macOS: `librust_fontconfig.dylib` and `librust_fontconfig.a`\n- Linux: `librust_fontconfig.so` and `librust_fontconfig.a`\n\n#### Building from Source\n\nAlternatively, you can build the library from source:\n\n```bash\n# Clone the repository\ngit clone https://github.com/maps4print/rust-fontconfig.git\ncd rust-fontconfig\n\n# Build with FFI support\ncargo build --release --features ffi\n\n# The generated libraries will be in target/release\n```\n\n#### Including in Your C Project\n\n1. Copy the header file from `ffi/rust_fontconfig.h` to your include directory\n2. Link against the static or dynamic library\n3. Include the header file in your C code:\n\n```c\n#include \"rust_fontconfig.h\"\n```\n\n### Minimal C Example\n\n```c\n#include \u003cstdio.h\u003e\n#include \"rust_fontconfig.h\"\n\nint main() {\n    // Build the font cache\n    FcFontCache cache = fc_cache_build();\n    if (!cache) {\n        fprintf(stderr, \"Failed to build font cache\\n\");\n        return 1;\n    }\n    \n    // Create a pattern to search for Arial\n    FcPattern* pattern = fc_pattern_new();\n    fc_pattern_set_name(pattern, \"Arial\");\n    \n    // Search for the font\n    FcTraceMsg* trace = NULL;\n    size_t trace_count = 0;\n    FcFontMatch* match = fc_cache_query(cache, pattern, \u0026trace, \u0026trace_count);\n    \n    if (match) {\n        char id_str[40];\n        fc_font_id_to_string(\u0026match-\u003eid, id_str, sizeof(id_str));\n        printf(\"Found font! ID: %s\\n\", id_str);\n        \n        // Get the font path\n        FcFontPath* font_path = fc_cache_get_font_path(cache, \u0026match-\u003eid);\n        if (font_path) {\n            printf(\"Font path: %s (index: %zu)\\n\", font_path-\u003epath, font_path-\u003efont_index);\n            fc_font_path_free(font_path);\n        }\n        \n        fc_font_match_free(match);\n    } else {\n        printf(\"Font not found\\n\");\n    }\n    \n    // Clean up\n    fc_pattern_free(pattern);\n    if (trace) fc_trace_free(trace, trace_count);\n    fc_cache_free(cache);\n    \n    return 0;\n}\n```\n\nFor a more comprehensive example, see the [example.c](ffi/example.c) file included in the repository.\n\n#### Compiling the C Example\n\nOn Linux:\n```bash\ngcc -I./include -L. -o font_example example.c -lrust_fontconfig\n```\n\nOn macOS:\n```bash\nclang -I./include -L. -o font_example example.c -lrust_fontconfig\n```\n\nOn Windows:\n```bash\ncl.exe /I./include /Fe:font_example.exe example.c rust_fontconfig.lib\n```\n\n## Performance\n\n- cache building: ~90ms for ~530 fonts\n- cache query: ~4µs\n\n## Features\n\n- **Font matching** by name, family, style properties, or Unicode ranges\n- **CSS font-family resolution** with `resolve_font_chain()` for proper fallback handling\n- **Per-character font resolution** with `chain.resolve_text()` for multilingual text\n- **Font run grouping** with `chain.query_for_text()` for text shaping pipelines\n- Support for font weights (thin, light, normal, bold, etc.)\n- Support for font stretches (condensed, normal, expanded, etc.)\n- In-memory font loading and caching\n- Optional `no_std` support (\"bring your own fonts\" for WASM)\n- C API for integration with non-Rust languages\n\n## License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschutt%2Frust-fontconfig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffschutt%2Frust-fontconfig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschutt%2Frust-fontconfig/lists"}