integration_api/
integration_api.rs1use rust_fontconfig::{FcFontCache, FcWeight, PatternMatch, FontId};
6
7fn main() {
8 println!("Font Integration API Example\n");
9
10 println!("Step 1: Building font cache...");
12 let cache = FcFontCache::build();
13 println!(" Loaded {} fonts\n", cache.list().len());
14
15 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, PatternMatch::False, &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 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 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 println!("\n\nStep 4: Loading fonts for shaping");
102
103 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 if let Some(source) = cache.get_font_by_id(font_id) {
115 match source {
116 rust_fontconfig::FontSource::Disk(path) => {
117 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}