Skip to main content

integration_api/
integration_api.rs

1//! Integration API example
2//!
3//! Shows the complete workflow for integrating rust-fontconfig into a text
4//! layout pipeline — from CSS font-family resolution to loading font bytes.
5//!
6//! Run with:
7//!   cargo run --example integration_api
8
9use rust_fontconfig::{FcFontCache, FcWeight, FontId, PatternMatch};
10
11fn main() {
12    println!("=== Font Integration Pipeline ===\n");
13
14    // ── Step 1: Build font cache ──
15    println!("Step 1: Build font cache");
16    let cache = FcFontCache::build();
17    println!("  {} fonts loaded\n", cache.list().len());
18
19    // ── Step 2: Resolve CSS font-family ──
20    let css_families = vec![
21        "Helvetica".to_string(),
22        "Arial".to_string(),
23        "sans-serif".to_string(),
24    ];
25    println!("Step 2: Resolve font-family: {:?}\n", css_families);
26
27    let chain = cache.resolve_font_chain(
28        &css_families,
29        FcWeight::Normal,
30        PatternMatch::False,
31        PatternMatch::False,
32        &mut Vec::new(),
33    );
34
35    for (i, group) in chain.css_fallbacks.iter().enumerate() {
36        print!("  [{}] '{}': {} fonts", i + 1, group.css_name, group.fonts.len());
37        if let Some(first) = group.fonts.first() {
38            if let Some(meta) = cache.get_metadata_by_id(&first.id) {
39                print!(
40                    " (first: {:?})",
41                    meta.name.as_ref().or(meta.family.as_ref())
42                );
43            }
44        }
45        println!();
46    }
47    println!(
48        "  + {} unicode fallback fonts\n",
49        chain.unicode_fallbacks.len()
50    );
51
52    // ── Step 3: Resolve text to font runs ──
53    let text = "Hello 世界! Привет мир";
54    println!("Step 3: Resolve text: '{}'\n", text);
55
56    let resolved = chain.resolve_text(&cache, text);
57
58    // Group by runs of same font
59    let mut runs: Vec<(String, Option<FontId>)> = Vec::new();
60    let mut current_text = String::new();
61    let mut current_id: Option<FontId> = None;
62
63    for (ch, info) in &resolved {
64        let this_id = info.as_ref().map(|(id, _)| *id);
65        if this_id != current_id {
66            if !current_text.is_empty() {
67                runs.push((current_text.clone(), current_id));
68                current_text.clear();
69            }
70            current_id = this_id;
71        }
72        current_text.push(*ch);
73    }
74    if !current_text.is_empty() {
75        runs.push((current_text, current_id));
76    }
77
78    println!("  Font runs:");
79    for (run_text, font_id) in &runs {
80        let name = font_id
81            .as_ref()
82            .and_then(|id| cache.get_metadata_by_id(id))
83            .and_then(|m| m.name.clone().or(m.family.clone()))
84            .unwrap_or_else(|| "[NO FONT]".into());
85        println!("    '{}' -> {}", run_text, name);
86    }
87
88    // ── Step 4: Load font bytes ──
89    let unique_fonts: std::collections::HashSet<_> =
90        runs.iter().filter_map(|(_, id)| *id).collect();
91
92    println!(
93        "\nStep 4: Load fonts ({} unique needed)\n",
94        unique_fonts.len()
95    );
96    for font_id in &unique_fonts {
97        if let Some(meta) = cache.get_metadata_by_id(font_id) {
98            let name = meta
99                .name
100                .as_ref()
101                .or(meta.family.as_ref())
102                .map(|s| s.as_str())
103                .unwrap_or("?");
104            if let Some(source) = cache.get_font_by_id(font_id) {
105                match source {
106                    rust_fontconfig::FontSource::Disk(path) => {
107                        println!("  {} -> {}", name, path.path);
108                    }
109                    rust_fontconfig::FontSource::Memory(font) => {
110                        println!("  {} -> memory (id: {})", name, font.id);
111                    }
112                }
113            }
114        }
115    }
116
117    println!("\nPipeline summary:");
118    println!("  1. FcFontCache::build()       — once at startup");
119    println!("  2. cache.resolve_font_chain() — per CSS font-family");
120    println!("  3. chain.resolve_text()       — per text run");
121    println!("  4. cache.get_font_by_id()     — load bytes for shaping");
122}