u8g2_fonts/content/
args.rs1use core::{mem, ops::Range};
2
3use crate::{
4 font_reader::FontReader,
5 renderer::render_actions::compute_horizontal_glyph_dimensions,
6 utils::{FormatArgsReader, FormatArgsReaderInfallible, HorizontalRenderedDimensions},
7 Content, LookupError,
8};
9
10use super::LineDimensionsIterator;
11
12impl<'a> Content for core::fmt::Arguments<'a> {
13 fn for_each_char<F, E>(&self, mut func: F) -> Result<(), E>
14 where
15 F: FnMut(char) -> Result<(), E>,
16 {
17 FormatArgsReader::new(|ch| func(ch).map(|()| true)).process_args(*self)
18 }
19
20 fn for_each_char_infallible<F>(&self, func: F)
21 where
22 F: FnMut(char),
23 {
24 FormatArgsReaderInfallible::new(func).process_args(*self)
25 }
26
27 type LineDimensionsIter = ArgsLineDimensionsIterator<'a>;
28
29 fn line_dimensions_iterator(&self) -> ArgsLineDimensionsIterator<'a> {
30 ArgsLineDimensionsIterator::new(*self)
31 }
32}
33
34const NUM_BUFFERED_LINES: usize = 5;
39
40pub struct ArgsLineDimensionsIterator<'a> {
41 args: core::fmt::Arguments<'a>,
42 buffer_range: Range<usize>,
43 dimensions_buffer: [HorizontalRenderedDimensions; NUM_BUFFERED_LINES],
44 next_line: usize,
45 finished: bool,
46}
47
48impl<'a> ArgsLineDimensionsIterator<'a> {
49 pub fn new(args: core::fmt::Arguments<'a>) -> Self {
50 Self {
51 args,
52 buffer_range: 0..0,
53 dimensions_buffer: [(); NUM_BUFFERED_LINES]
54 .map(|()| HorizontalRenderedDimensions::empty()),
55 next_line: 0,
56 finished: false,
57 }
58 }
59
60 pub fn regenerate_buffer(
61 &mut self,
62 range_start: usize,
63 font: &FontReader,
64 ) -> Result<(), LookupError> {
65 let mut line_dimensions = HorizontalRenderedDimensions::empty();
66 let mut line_num: usize = 0;
67
68 FormatArgsReader::new(|ch| -> Result<bool, LookupError> {
69 if ch == '\n' {
70 let previous_line_dimensions =
71 mem::replace(&mut line_dimensions, HorizontalRenderedDimensions::empty());
72
73 if let Some(array_pos) = line_num.checked_sub(range_start) {
74 if let Some(cell) = self.dimensions_buffer.get_mut(array_pos) {
75 *cell = previous_line_dimensions;
77 }
78 }
79
80 line_num += 1;
81
82 if line_num >= range_start + self.dimensions_buffer.len() {
83 return Ok(false);
85 }
86 } else if line_num >= range_start {
87 let dimensions =
89 compute_horizontal_glyph_dimensions(ch, line_dimensions.advance, font)?;
90 line_dimensions.add(dimensions);
91 }
92
93 Ok(true)
94 })
95 .process_args(self.args)?;
96
97 if let Some(array_pos) = line_num.checked_sub(range_start) {
99 if let Some(cell) = self.dimensions_buffer.get_mut(array_pos) {
100 *cell = line_dimensions;
102
103 self.finished = true;
105 line_num += 1;
106 }
107 }
108
109 self.buffer_range = range_start..line_num;
110 assert!(self.buffer_range.len() <= self.dimensions_buffer.len());
111
112 Ok(())
113 }
114}
115
116impl LineDimensionsIterator for ArgsLineDimensionsIterator<'_> {
117 fn next(
118 &mut self,
119 font: &crate::font_reader::FontReader,
120 ) -> Result<HorizontalRenderedDimensions, LookupError> {
121 let next_line = self.next_line;
122 self.next_line += 1;
123
124 if !self.buffer_range.contains(&next_line) {
125 if self.finished {
126 return Ok(HorizontalRenderedDimensions::empty());
127 }
128
129 self.regenerate_buffer(next_line, font)?;
130 assert!(self.buffer_range.contains(&next_line));
131 }
132
133 Ok(self.dimensions_buffer[next_line - self.buffer_range.start].clone())
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 extern crate std;
140 use core::fmt::Arguments;
141 use std::vec::Vec;
142
143 use crate::fonts;
144
145 use super::*;
146
147 #[test]
148 fn for_each_char_produces_correct_values() {
149 let mut content = Vec::new();
150
151 format_args!("{}", "abc")
152 .for_each_char(|e| {
153 content.push(e);
154 Result::<(), &'static str>::Ok(())
155 })
156 .unwrap();
157
158 assert_eq!(content, ['a', 'b', 'c']);
159 }
160
161 #[test]
162 fn for_each_char_infallible_produces_correct_values() {
163 let mut content = Vec::new();
164
165 format_args!("{}", "abc").for_each_char_infallible(|e| {
166 content.push(e);
167 });
168
169 assert_eq!(content, ['a', 'b', 'c']);
170 }
171
172 #[test]
173 fn for_each_char_propagates_error() {
174 let result = format_args!("{}", "abc").for_each_char(|_| Err("Failed!"));
175
176 assert_eq!(result, Err("Failed!"));
177 }
178
179 #[test]
180 fn get_newline_count_provides_correct_value() {
181 assert_eq!(format_args!("{}", "a\nbc\n").get_newline_count(), 2);
182 assert_eq!(format_args!("{}", "a\nbc").get_newline_count(), 1);
183 assert_eq!(format_args!("{}", "").get_newline_count(), 0);
184 }
185
186 #[test]
187 fn line_dimensions_iter_provides_correct_values() {
188 fn run_test(args: Arguments<'_>) {
190 let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
191 let mut dims = args.line_dimensions_iterator();
192
193 assert_eq!(
194 dims.next(&font).unwrap(),
195 HorizontalRenderedDimensions {
196 advance: 4,
197 bounding_box_width: 3,
198 bounding_box_offset: 0,
199 }
200 );
201 assert_eq!(
202 dims.next(&font).unwrap(),
203 HorizontalRenderedDimensions {
204 advance: 7,
205 bounding_box_width: 6,
206 bounding_box_offset: 0,
207 }
208 );
209 assert_eq!(
210 dims.next(&font).unwrap(),
211 HorizontalRenderedDimensions::empty()
212 );
213 assert_eq!(
214 dims.next(&font).unwrap(),
215 HorizontalRenderedDimensions::empty()
216 );
217 }
218
219 run_test(format_args!("{}", "a\nbc\n"));
220 }
221
222 #[test]
223 fn line_dimensions_iter_errors_on_glyph_not_found() {
224 fn run_test(args: Arguments<'_>) {
226 let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
227 let mut dims = args.line_dimensions_iterator();
228
229 assert!(matches!(
230 dims.next(&font),
231 Err(LookupError::GlyphNotFound('☃'))
232 ));
233 }
234
235 run_test(format_args!("{}", "☃"));
236 }
237
238 #[test]
239 fn line_dimensions_iter_creates_empty_array_when_out_of_range() {
240 fn run_test(args: Arguments<'_>) {
242 let font = FontReader::new::<fonts::u8g2_font_u8glib_4_tf>();
243 let mut dims = args.line_dimensions_iterator();
244
245 dims.regenerate_buffer(1000, &font).unwrap();
246 assert!(dims.buffer_range.is_empty());
247 }
248
249 run_test(format_args!("{}", "a"));
250 }
251}