integration_api/
integration_api.rs

1//! Integration API example
2//! 
3//! Shows how to integrate rust-fontconfig into a text layout pipeline.
4
5use rust_fontconfig::{FcFontCache, FcWeight, PatternMatch, FontId};
6
7fn main() {
8    println!("Font Integration API Example\n");
9    
10    // Step 1: Build the font cache once at application startup
11    println!("Step 1: Building font cache...");
12    let cache = FcFontCache::build();
13    println!("  Loaded {} fonts\n", cache.list().len());
14    
15    // Step 2: Simulate CSS font-family resolution
16    // This is what a browser/text renderer would do
17    println!("Step 2: Resolving CSS font-family: 'Helvetica, Arial, sans-serif'\n");
18    
19    let css_families = vec![
20        "Helvetica".to_string(),
21        "Arial".to_string(),
22        "sans-serif".to_string(),
23    ];
24    
25    let mut trace = Vec::new();
26    let chain = cache.resolve_font_chain(
27        &css_families,
28        FcWeight::Normal,
29        PatternMatch::False,  // italic
30        PatternMatch::False,  // oblique
31        &mut trace,
32    );
33    
34    println!("  CSS fallback groups: {}", chain.css_fallbacks.len());
35    for (i, group) in chain.css_fallbacks.iter().enumerate() {
36        println!("    {}: CSS '{}' -> {} fonts", i + 1, group.css_name, group.fonts.len());
37        for font in group.fonts.iter().take(2) {
38            if let Some(meta) = cache.get_metadata_by_id(&font.id) {
39                let name = meta.name.as_ref().or(meta.family.as_ref());
40                println!("       - {:?}", name);
41            }
42        }
43        if group.fonts.len() > 2 {
44            println!("       ... and {} more", group.fonts.len() - 2);
45        }
46    }
47    
48    println!("\n  Unicode fallback fonts: {}", chain.unicode_fallbacks.len());
49    for (i, font) in chain.unicode_fallbacks.iter().take(3).enumerate() {
50        if let Some(meta) = cache.get_metadata_by_id(&font.id) {
51            println!("    {}: {:?}", 
52                     i + 1, 
53                     meta.name.as_ref().or(meta.family.as_ref()));
54        }
55    }
56    if chain.unicode_fallbacks.len() > 3 {
57        println!("    ... and {} more", chain.unicode_fallbacks.len() - 3);
58    }
59    
60    // Step 3: Resolve text to fonts
61    // This maps each character to a specific font
62    println!("\n\nStep 3: Resolve text to fonts");
63    
64    let text = "Hello 世界! Привет мир";
65    println!("  Input text: '{}'\n", text);
66    
67    let resolved = chain.resolve_text(&cache, text);
68    
69    // Group by runs of same font
70    let mut runs = Vec::new();
71    let mut current_run_text = String::new();
72    let mut current_font_id: Option<FontId> = None;
73    
74    for (ch, font_info) in &resolved {
75        let this_font_id = font_info.as_ref().map(|(id, _)| *id);
76        
77        if this_font_id != current_font_id {
78            if !current_run_text.is_empty() {
79                runs.push((current_run_text.clone(), current_font_id));
80                current_run_text.clear();
81            }
82            current_font_id = this_font_id;
83        }
84        current_run_text.push(*ch);
85    }
86    if !current_run_text.is_empty() {
87        runs.push((current_run_text, current_font_id));
88    }
89    
90    println!("  Font runs:");
91    for (run_text, font_id) in &runs {
92        let font_name = font_id.as_ref()
93            .and_then(|id| cache.get_metadata_by_id(id))
94            .and_then(|m| m.name.clone().or(m.family.clone()))
95            .unwrap_or_else(|| "[NO FONT]".to_string());
96        println!("    '{}' -> {}", run_text, font_name);
97    }
98    
99    // Step 4: Load font data for shaping
100    // In a real application, you'd load font bytes here
101    println!("\n\nStep 4: Loading fonts for shaping");
102    
103    // Collect unique font IDs needed for this text
104    let unique_fonts: std::collections::HashSet<_> = runs.iter()
105        .filter_map(|(_, id)| *id)
106        .collect();
107    
108    println!("  Unique fonts needed: {}", unique_fonts.len());
109    
110    for font_id in &unique_fonts {
111        if let Some(meta) = cache.get_metadata_by_id(font_id) {
112            println!("    - {:?}", meta.name.as_ref().or(meta.family.as_ref()));
113            // Get font path via get_font_by_id
114            if let Some(source) = cache.get_font_by_id(font_id) {
115                match source {
116                    rust_fontconfig::FontSource::Disk(path) => {
117                        // In real code, you'd load the font file here:
118                        // let bytes = std::fs::read(&path.path)?;
119                        // let parsed = ttf_parser::Face::parse(&bytes, path.font_index as u32)?;
120                        println!("      Path: {}", path.path);
121                    }
122                    rust_fontconfig::FontSource::Memory(font) => {
123                        println!("      Memory font (id: {})", font.id);
124                    }
125                }
126            }
127        }
128    }
129    
130    println!("\nWorkflow summary:");
131    println!("");
132    println!("1. FcFontCache::build() - once at startup");
133    println!("2. cache.resolve_font_chain() - per CSS font-family declaration");
134    println!("3. chain.resolve_text(\"abc\") -> [Run {{ font, glyphs: \"abc\" }}] - per text string to shape");
135    println!("4. Load font bytes and shape each run with its font");
136}