Skip to main content

unicode_aware_fonts/
unicode_aware_fonts.rs

1//! Unicode-aware font resolution
2//!
3//! Shows how font chains handle multi-script text by automatically selecting
4//! the right font for each character (Latin, CJK, Arabic, Cyrillic, etc.).
5//!
6//! Run with:
7//!   cargo run --example unicode_aware_fonts
8
9use rust_fontconfig::{FcFontCache, FcWeight, FontFallbackChain, PatternMatch};
10
11fn main() {
12    let cache = FcFontCache::build();
13
14    println!("=== Unicode-Aware Font Selection ===\n");
15
16    // Create a font chain for sans-serif
17    let chain = cache.resolve_font_chain(
18        &["sans-serif".to_string()],
19        FcWeight::Normal,
20        PatternMatch::False,
21        PatternMatch::False,
22        &mut Vec::new(),
23    );
24
25    println!(
26        "Font chain: {} CSS fallbacks, {} unicode fallbacks\n",
27        chain.css_fallbacks.len(),
28        chain.unicode_fallbacks.len()
29    );
30
31    // Resolve different scripts
32    let texts = [
33        ("Latin", "Hello World"),
34        ("CJK", "你好世界"),
35        ("Japanese", "こんにちは世界"),
36        ("Arabic", "مرحبا بالعالم"),
37        ("Cyrillic", "Привет мир"),
38        ("Mixed", "Hello 世界 Привет"),
39    ];
40
41    for (label, text) in &texts {
42        println!("{} text: '{}'", label, text);
43        print_resolution(&cache, &chain, text);
44        println!();
45    }
46
47    println!("Workflow:");
48    println!("  1. resolve_font_chain() — creates fallback chain from CSS font-family");
49    println!("  2. chain.resolve_text()  — maps each character to a font");
50    println!("  3. Use font IDs to load and render glyphs");
51}
52
53fn print_resolution(cache: &FcFontCache, chain: &FontFallbackChain, text: &str) {
54    let resolved = chain.resolve_text(cache, text);
55
56    let mut current_font: Option<String> = None;
57    let mut segment = String::new();
58
59    for (ch, info) in &resolved {
60        let font_name = info.as_ref().and_then(|(id, _)| {
61            cache
62                .get_metadata_by_id(id)
63                .and_then(|m| m.name.clone().or(m.family.clone()))
64        });
65        if font_name != current_font {
66            if !segment.is_empty() {
67                println!(
68                    "  '{}' -> {}",
69                    segment,
70                    current_font.as_deref().unwrap_or("[NO FONT]")
71                );
72                segment.clear();
73            }
74            current_font = font_name;
75        }
76        segment.push(*ch);
77    }
78    if !segment.is_empty() {
79        println!(
80            "  '{}' -> {}",
81            segment,
82            current_font.as_deref().unwrap_or("[NO FONT]")
83        );
84    }
85}