1use super::FontRegistry;
10
11#[derive(Debug, Clone)]
13pub struct FontRun {
14 pub start: usize,
16 pub end: usize,
18 pub family: String,
20}
21
22pub fn segment_by_font(
30 chars: &[char],
31 families: &str,
32 weight: u32,
33 italic: bool,
34 registry: &FontRegistry,
35) -> Vec<FontRun> {
36 if chars.is_empty() {
37 return vec![];
38 }
39
40 if !families.contains(',') {
42 let family = families.trim().trim_matches('"').trim_matches('\'');
43 let font = registry.resolve(family, weight, italic);
44 let all_covered = chars
45 .iter()
46 .all(|&ch| ch.is_whitespace() || font.has_char(ch));
47 if all_covered {
48 return vec![FontRun {
49 start: 0,
50 end: chars.len(),
51 family: family.to_string(),
52 }];
53 }
54 }
57
58 let mut runs = Vec::new();
60 let (_, first_family) = registry.resolve_for_char(families, chars[0], weight, italic);
61 let mut current_family = first_family;
62 let mut run_start = 0;
63
64 for (i, &ch) in chars.iter().enumerate().skip(1) {
65 let (_, family) = registry.resolve_for_char(families, ch, weight, italic);
66 if family != current_family {
67 runs.push(FontRun {
68 start: run_start,
69 end: i,
70 family: current_family,
71 });
72 current_family = family;
73 run_start = i;
74 }
75 }
76
77 runs.push(FontRun {
79 start: run_start,
80 end: chars.len(),
81 family: current_family,
82 });
83
84 runs
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_single_font_fast_path() {
93 let registry = FontRegistry::new();
94 let chars: Vec<char> = "Hello world".chars().collect();
95 let runs = segment_by_font(&chars, "Helvetica", 400, false, ®istry);
96 assert_eq!(runs.len(), 1);
97 assert_eq!(runs[0].family, "Helvetica");
98 assert_eq!(runs[0].start, 0);
99 assert_eq!(runs[0].end, 11);
100 }
101
102 #[test]
103 fn test_empty_input() {
104 let registry = FontRegistry::new();
105 let chars: Vec<char> = vec![];
106 let runs = segment_by_font(&chars, "Helvetica, Times", 400, false, ®istry);
107 assert!(runs.is_empty());
108 }
109
110 #[test]
111 fn test_single_font_builtin_fallback() {
112 let registry = FontRegistry::new();
113 let chars: Vec<char> = "\u{041F}\u{0440}\u{0438}\u{0432}\u{0435}\u{0442}"
115 .chars()
116 .collect();
117 let runs = segment_by_font(&chars, "Helvetica", 400, false, ®istry);
118 assert!(runs.len() >= 1, "Should produce at least one run");
119 assert_eq!(runs[0].family, "Noto Sans", "Cyrillic should use Noto Sans");
121 }
122
123 #[test]
124 fn test_single_font_mixed_latin_cyrillic() {
125 let registry = FontRegistry::new();
126 let chars: Vec<char> = "Hi \u{041F}".chars().collect();
128 let runs = segment_by_font(&chars, "Helvetica", 400, false, ®istry);
129 assert!(
130 runs.len() >= 2,
131 "Should have at least 2 runs (Latin + Cyrillic), got {}",
132 runs.len()
133 );
134 }
135
136 #[test]
137 fn test_all_chars_same_font() {
138 let registry = FontRegistry::new();
139 let chars: Vec<char> = "ABC".chars().collect();
140 let runs = segment_by_font(&chars, "Helvetica, Times", 400, false, ®istry);
142 assert_eq!(runs.len(), 1);
143 assert_eq!(runs[0].family, "Helvetica");
144 }
145}