| Crates.io | rust-fontconfig |
| lib.rs | rust-fontconfig |
| version | 1.2.1 |
| created_at | 2021-02-28 13:32:57.708097+00 |
| updated_at | 2025-11-25 23:33:46.979504+00 |
| description | Pure-Rust alternative to font-loader and fontconfig w. minimal dependencies |
| homepage | |
| repository | https://github.com/fschutt/rust-fontconfig |
| max_upload_size | |
| id | 361786 |
| size | 301,417 |
Pure-Rust rewrite of the Linux fontconfig library (no system dependencies) - using allsorts as a font parser to support .woff, .woff2, .ttc, .otf and .ttf
NOTE: Also works on Windows, macOS and WASM - without external dependencies!
There are a number of reasons why I want to have a pure-Rust version of fontconfig:
no_std support ("bring your own font files") for WASMNow for the more practical reasons:
use rust_fontconfig::{FcFontCache, FcPattern};
fn main() {
// Build the font cache (scans system fonts)
let cache = FcFontCache::build();
// Query a font by name
let mut trace = Vec::new();
let results = cache.query(
&FcPattern {
name: Some(String::from("Arial")),
..Default::default()
},
&mut trace
);
if let Some(font_match) = results {
println!("Font match ID: {:?}", font_match.id);
println!("Font unicode ranges: {:?}", font_match.unicode_ranges);
// Get font metadata
if let Some(meta) = cache.get_metadata_by_id(&font_match.id) {
println!("Family: {:?}", meta.family);
}
// Get font file path
if let Some(source) = cache.get_font_by_id(&font_match.id) {
match source {
rust_fontconfig::FontSource::Disk(path) => {
println!("Path: {}", path.path);
}
rust_fontconfig::FontSource::Memory(font) => {
println!("Memory font: {}", font.id);
}
}
}
} else {
println!("No matching font found");
}
}
The new API separates font chain resolution from text querying:
resolve_font_chain() - Create a fallback chain from CSS font-family (without text)chain.resolve_text() - Query which fonts to use for specific textuse rust_fontconfig::{FcFontCache, FcWeight, PatternMatch};
fn main() {
let cache = FcFontCache::build();
// Step 1: Build font fallback chain (without text parameter)
let mut trace = Vec::new();
let font_chain = cache.resolve_font_chain(
&["Arial".to_string(), "sans-serif".to_string()],
FcWeight::Normal,
PatternMatch::DontCare, // italic
PatternMatch::DontCare, // oblique
&mut trace,
);
println!("CSS fallback groups: {}", font_chain.css_fallbacks.len());
for group in &font_chain.css_fallbacks {
println!(" CSS '{}' resolved to {} fonts", group.css_name, group.fonts.len());
}
// Step 2: Query which fonts to use for specific text
let text = "Hello 你好 Здравствуйте";
let font_runs = font_chain.query_for_text(&cache, text);
println!("\nText '{}' split into {} font runs:", text, font_runs.len());
for run in &font_runs {
println!(" '{}' -> font {:?}", run.text, run.font_id);
}
}
For fine-grained control, use resolve_text() to get per-character font assignments:
use rust_fontconfig::{FcFontCache, FcWeight, PatternMatch};
fn main() {
let cache = FcFontCache::build();
let chain = cache.resolve_font_chain(
&["sans-serif".to_string()],
FcWeight::Normal,
PatternMatch::False,
PatternMatch::False,
&mut Vec::new(),
);
// Get font assignment for each character
let text = "Hello 世界";
let resolved = chain.resolve_text(&cache, text);
for (ch, font_info) in resolved {
match font_info {
Some((font_id, css_source)) => {
let font_name = cache.get_metadata_by_id(&font_id)
.and_then(|m| m.name.clone().or(m.family.clone()))
.unwrap_or_default();
println!("'{}' -> {} (from CSS '{}')", ch, font_name, css_source);
}
None => println!("'{}' -> NO FONT FOUND", ch),
}
}
}
use rust_fontconfig::{FcFontCache, FcWeight};
fn main() {
let cache = FcFontCache::build();
// List all fonts - filter by properties
let bold_fonts: Vec<_> = cache.list().into_iter()
.filter(|(pattern, _id)| {
matches!(pattern.weight, FcWeight::Bold | FcWeight::ExtraBold)
})
.collect();
println!("Found {} bold fonts:", bold_fonts.len());
for (pattern, id) in bold_fonts.iter().take(5) {
println!(" {:?}: {:?}", id, pattern.name.as_ref().or(pattern.family.as_ref()));
}
}
The rust-fontconfig library provides C-compatible bindings that can be used from C/C++ applications.
You can download pre-built binary files from the latest GitHub release:
rust_fontconfig.dll and rust_fontconfig.liblibrust_fontconfig.dylib and librust_fontconfig.alibrust_fontconfig.so and librust_fontconfig.aAlternatively, you can build the library from source:
# Clone the repository
git clone https://github.com/maps4print/rust-fontconfig.git
cd rust-fontconfig
# Build with FFI support
cargo build --release --features ffi
# The generated libraries will be in target/release
ffi/rust_fontconfig.h to your include directory#include "rust_fontconfig.h"
#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;
}
For a more comprehensive example, see the example.c file included in the repository.
On Linux:
gcc -I./include -L. -o font_example example.c -lrust_fontconfig
On macOS:
clang -I./include -L. -o font_example example.c -lrust_fontconfig
On Windows:
cl.exe /I./include /Fe:font_example.exe example.c rust_fontconfig.lib
resolve_font_chain() for proper fallback handlingchain.resolve_text() for multilingual textchain.query_for_text() for text shaping pipelinesno_std support ("bring your own fonts" for WASM)MIT