limn_text_layout/
line.rs

1
2/// Text handling logic related to individual lines of text.
3///
4/// This module is the core of multi-line text handling.
5use rusttype;
6use super::Font;
7use rusttype::Scale;
8use types::{Range, Align, Rect, RectExt};
9use std;
10use rusttype::GlyphId;
11use std::str::CharIndices;
12use std::iter::Peekable;
13use super::Wrap;
14use super::glyph::SelectedGlyphRectsPerLine;
15
16#[derive(Copy, Clone, Debug, PartialEq)]
17enum BreakType {
18    /// A break caused by the text exceeding some maximum width.
19    Wrap {
20        /// The byte length which should be skipped in order to reach the first non-whitespace
21        /// character to use as the beginning of the next line.
22        len_bytes: usize,
23    },
24    /// A break caused by a newline character.
25    Newline {
26        /// The width of the "newline" token in bytes.
27        len_bytes: usize,
28    },
29    End,
30}
31#[derive(Copy, Clone, Debug, PartialEq)]
32pub struct Break {
33    /// The byte index at which the line ends.
34    byte: usize,
35    /// The char index at which the line ends.
36    char: usize,
37    break_type: BreakType,
38}
39impl Break {
40    fn new(byte: usize, char: usize, break_type: BreakType) -> Self {
41        Break {
42            byte: byte,
43            char: char,
44            break_type: break_type,
45        }
46    }
47}
48
49/// Information about a single line of text within a `&str`.
50///
51/// `Info` is a minimal amount of information that can be stored for efficient reasoning about
52/// blocks of text given some `&str`. The `start` and `end_break` can be used for indexing into
53/// the `&str`, and the `width` can be used for calculating line `Rect`s, alignment, etc.
54#[derive(Copy, Clone, Debug, PartialEq)]
55pub struct LineInfo {
56    /// The index into the `&str` that represents the first character within the line.
57    pub start_byte: usize,
58    /// The character index of the first character in the line.
59    pub start_char: usize,
60    /// The index within the `&str` at which this line breaks into a new line, along with the
61    /// index at which the following line begins. The variant describes whether the break is
62    /// caused by a `Newline` character or a `Wrap` by the given wrap function.
63    pub end_break: Break,
64    /// The total width of all characters within the line.
65    pub width: f32,
66}
67
68impl LineInfo {
69    /// The end of the byte index range for indexing into the slice.
70    pub fn end_byte(&self) -> usize {
71        self.end_break.byte
72    }
73
74    /// The end of the index range for indexing into the slice.
75    pub fn end_char(&self) -> usize {
76        self.end_break.char
77    }
78
79    /// The index range for indexing (via bytes) into the original str slice.
80    pub fn byte_range(self) -> std::ops::Range<usize> {
81        self.start_byte..self.end_byte()
82    }
83
84    /// The index range for indexing into a `char` iterator over the original str slice.
85    pub fn char_range(self) -> std::ops::Range<usize> {
86        self.start_char..self.end_char()
87    }
88}
89
90/// An iterator yielding an `Info` struct for each line in the given `text` wrapped by the
91/// given `next_break_fn`.
92///
93/// `Infos` is a fundamental part of performing lazy reasoning about text in this library.
94///
95/// Construct an `Infos` iterator via the [`infos` function](./fn.infos.html) and its two builder
96/// methods, [`wrap_by_character`](./struct.Infos.html#method.wrap_by_character) and
97/// [`wrap_by_whitespace`](./struct.Infos.html#method.wrap_by_whitespace).
98#[derive(Copy, Clone)]
99pub struct LineInfos<'a> {
100    text: &'a str,
101    font: &'a Font,
102    font_size: f32,
103    max_width: f32,
104    line_wrap: Wrap,
105    /// The index that indicates the start of the next line to be yielded.
106    start_byte: usize,
107    /// The character index that indicates the start of the next line to be yielded.
108    start_char: usize,
109    /// The break type of the previously yielded line
110    last_break: Option<Break>,
111}
112
113impl<'a> LineInfos<'a> {
114    pub fn new(text: &'a str,
115               font: &'a Font,
116               font_size: f32,
117               line_wrap: Wrap,
118               max_width: f32)
119               -> Self {
120        LineInfos {
121            text: text,
122            font: font,
123            font_size: font_size,
124            max_width: max_width,
125            line_wrap: line_wrap,
126            start_byte: 0,
127            start_char: 0,
128            last_break: None,
129        }
130    }
131}
132
133impl<'a> Iterator for LineInfos<'a> {
134    type Item = LineInfo;
135    fn next(&mut self) -> Option<Self::Item> {
136        let LineInfos { text,
137                        font,
138                        font_size,
139                        max_width,
140                        line_wrap,
141                        ref mut start_byte,
142                        ref mut start_char,
143                        ref mut last_break } = *self;
144
145        let text_line = &text[*start_byte..];
146        let (next, width) = match line_wrap {
147            Wrap::NoWrap => next_break(text_line, font, font_size),
148            Wrap::Character => next_break_by_character(text_line, font, font_size, max_width),
149            Wrap::Whitespace => next_break_by_whitespace(text_line, font, font_size, max_width),
150        };
151        match next.break_type {
152            BreakType::Newline { len_bytes } |
153            BreakType::Wrap { len_bytes } => {
154                if next.byte == 0 && len_bytes == 0 {
155                    None
156                } else {
157                    let next_break = Break::new(*start_byte + next.byte,
158                                                *start_char + next.char,
159                                                next.break_type);
160                    let info = LineInfo {
161                        start_byte: *start_byte,
162                        start_char: *start_char,
163                        end_break: next_break,
164                        width: width,
165                    };
166                    *start_byte = info.start_byte + next.byte + len_bytes;
167                    *start_char = info.start_char + next.char + 1;
168                    *last_break = Some(next_break);
169                    Some(info)
170                }
171            }
172            BreakType::End => {
173                let char = next.char;
174                // if the last line ends in a new line, or the entire text is empty,
175                // return an empty line Info
176                let empty_line = {
177                    match *last_break {
178                        Some(last_break_) => {
179                            match last_break_.break_type {
180                                BreakType::Newline { .. } => true,
181                                _ => false,
182                            }
183                        }
184                        None => true,
185                    }
186                };
187                if *start_byte < text.len() || empty_line {
188                    let total_bytes = text.len();
189                    let total_chars = *start_char + char;
190                    let end_break = Break::new(total_bytes, total_chars, BreakType::End);
191                    let info = LineInfo {
192                        start_byte: *start_byte,
193                        start_char: *start_char,
194                        end_break: end_break,
195                        width: width,
196                    };
197                    *start_byte = total_bytes;
198                    *start_char = total_chars;
199                    *last_break = Some(end_break);
200                    Some(info)
201                } else {
202                    None
203                }
204            }
205        }
206    }
207}
208
209/// An iterator yielding a `Rect` for each line in
210#[derive(Clone)]
211pub struct LineRects<I> {
212    infos: I,
213    align: Align,
214    line_height: f32,
215    next: Option<Rect>,
216}
217
218impl<I> LineRects<I>
219    where I: Iterator<Item = LineInfo> + ExactSizeIterator
220{
221    /// Produce an iterator yielding the bounding `Rect` for each line in the text.
222    ///
223    /// This function assumes that `font_size` is the same `FontSize` used to produce the `Info`s
224    /// yielded by the `infos` Iterator.
225    pub fn new(mut infos: I,
226               font_size: f32,
227               bounding_rect: Rect,
228               align: Align,
229               line_height: f32)
230               -> Self {
231        let num_lines = infos.len();
232        let first_rect = infos.next().map(|first_info| {
233            let bounding_x = bounding_rect.x_range();
234            let bounding_y = bounding_rect.y_range();
235            // Calculate the `x` `Range` of the first line `Rect`.
236            let range = Range::new(0.0, first_info.width);
237            let x = match align {
238                Align::Start => range.align_start_of(bounding_x),
239                Align::Middle => range.align_middle_of(bounding_x),
240                Align::End => range.align_end_of(bounding_x),
241            };
242
243            // Calculate the `y` `Range` of the first line `Rect`.
244            let total_text_height = num_lines as f32 * line_height;
245            let total_text_y_range = Range::new(0.0, total_text_height);
246            let total_text_y = total_text_y_range.align_start_of(bounding_y);
247            let range = Range::new(0.0, font_size);
248            let y = range.align_start_of(total_text_y);
249
250            Rect::from_ranges(x, y)
251        });
252
253        LineRects {
254            infos: infos,
255            next: first_rect,
256            align: align,
257            line_height: line_height,
258        }
259    }
260}
261
262impl<I> Iterator for LineRects<I>
263    where I: Iterator<Item = LineInfo>
264{
265    type Item = Rect;
266    fn next(&mut self) -> Option<Self::Item> {
267        let LineRects { ref mut next, ref mut infos, align, line_height } = *self;
268        next.map(|line_rect| {
269            *next = infos.next().map(|info| {
270                let y = Range::new(line_rect.bottom(), line_rect.bottom() + line_height);
271                let x = {
272                    let range = Range::new(0.0, info.width);
273                    match align {
274                        Align::Start => range.align_start_of(line_rect.x_range()),
275                        Align::Middle => range.align_middle_of(line_rect.x_range()),
276                        Align::End => range.align_end_of(line_rect.x_range()),
277                    }
278                };
279                Rect::from_ranges(x, y)
280            });
281
282            line_rect
283        })
284    }
285}
286
287/// An iterator yielding a `Rect` for each selected line in a block of text.
288///
289/// The yielded `Rect`s represent the selected range within each line of text.
290///
291/// Lines that do not contain any selected text will be skipped.
292pub struct SelectedLineRects<'a, I> {
293    selected_glyph_rects_per_line: SelectedGlyphRectsPerLine<'a, I>,
294}
295
296impl<'a, I> SelectedLineRects<'a, I>
297    where I: Iterator<Item = (&'a str, Rect)>
298{
299    /// Produces an iterator yielding a `Rect` for the selected range in each
300    /// selected line in a block of text.
301    ///
302    /// The yielded `Rect`s represent the selected range within each line of text.
303    ///
304    /// Lines that do not contain any selected text will be skipped.
305    pub fn new(lines_with_rects: I,
306               font: &'a Font,
307               font_size: f32,
308               start: super::cursor::Index,
309               end: super::cursor::Index)
310               -> SelectedLineRects<'a, I> {
311        SelectedLineRects {
312            selected_glyph_rects_per_line: SelectedGlyphRectsPerLine::new(lines_with_rects,
313                                                                          font,
314                                                                          font_size,
315                                                                          start,
316                                                                          end),
317        }
318    }
319}
320impl<'a, I> Iterator for SelectedLineRects<'a, I>
321    where I: Iterator<Item = (&'a str, Rect)>
322{
323    type Item = Rect;
324    fn next(&mut self) -> Option<Self::Item> {
325        while let Some(mut rects) = self.selected_glyph_rects_per_line.next() {
326            if let Some(first_rect) = rects.next() {
327                let total_selected_rect = rects.fold(first_rect, |mut total, next| {
328                    // TODO ?
329                    total.size.width = next.width();
330                    total
331                });
332                return Some(total_selected_rect);
333            }
334        }
335        None
336    }
337}
338
339/// A function for finding the advance width between the given character that also considers
340/// the kerning for some previous glyph.
341///
342/// This also updates the `last_glyph` with the glyph produced for the given `char`.
343///
344/// This is primarily for use within the `next_break` functions below.
345///
346/// The following code is adapted from the `rusttype::LayoutIter::next` src.
347fn advance_width(ch: char, font: &Font, scale: Scale, last_glyph: &mut Option<GlyphId>) -> f32 {
348    let g = font.glyph(ch).unwrap().scaled(scale);
349    let kern = last_glyph.map(|last| font.pair_kerning(scale, last, g.id()))
350        .unwrap_or(0.0);
351    let advance_width = g.h_metrics().advance_width;
352    *last_glyph = Some(g.id());
353    (kern + advance_width)
354}
355
356fn peek_next_char(char_indices: &mut Peekable<CharIndices>, next_char_expected: char) -> bool {
357    if let Some(&(_, next_char)) = char_indices.peek() {
358        next_char == next_char_expected
359    } else {
360        false
361    }
362}
363
364/// Returns the next index at which the text naturally breaks via a newline character,
365/// along with the width of the line.
366fn next_break(text: &str, font: &Font, font_size: f32) -> (Break, f32) {
367    let scale = super::pt_to_scale(font_size);
368    let mut width = 0.0;
369    let mut char_i = 0;
370    let mut char_indices = text.char_indices().peekable();
371    let mut last_glyph = None;
372    while let Some((byte_i, ch)) = char_indices.next() {
373        // Check for a newline.
374        if ch == '\r' && peek_next_char(&mut char_indices, '\n') {
375            let break_ = Break::new(byte_i, char_i, BreakType::Newline { len_bytes: 2 });
376            return (break_, width);
377        } else if ch == '\n' {
378            let break_ = Break::new(byte_i, char_i, BreakType::Newline { len_bytes: 1 });
379            return (break_, width);
380        }
381
382        // Update the width.
383        width += advance_width(ch, font, scale, &mut last_glyph);
384        char_i += 1;
385    }
386    let break_ = Break::new(text.len(), char_i, BreakType::End);
387    (break_, width)
388}
389/// Returns the next index at which the text will break by either:
390/// - A newline character.
391/// - A line wrap at the beginning of the first character exceeding the `max_width`.
392///
393/// Also returns the width of each line alongside the Break.
394fn next_break_by_character(text: &str,
395                           font: &Font,
396                           font_size: f32,
397                           max_width: f32)
398                           -> (Break, f32) {
399    let scale = super::pt_to_scale(font_size);
400    let mut width = 0.0;
401    let mut char_i = 0;
402    let mut char_indices = text.char_indices().peekable();
403    let mut last_glyph = None;
404    while let Some((byte_i, ch)) = char_indices.next() {
405        // Check for a newline.
406        if ch == '\r' && peek_next_char(&mut char_indices, '\n') {
407            let break_ = Break::new(byte_i, char_i, BreakType::Newline { len_bytes: 2 });
408            return (break_, width);
409        } else if ch == '\n' {
410            let break_ = Break::new(byte_i, char_i, BreakType::Newline { len_bytes: 1 });
411            return (break_, width);
412        }
413
414        // Add the character's width to the width so far.
415        let new_width = width + advance_width(ch, font, scale, &mut last_glyph);
416
417        // Check for a line wrap.
418        if new_width > max_width {
419            let break_ = Break::new(byte_i, char_i, BreakType::Wrap { len_bytes: 0 });
420            return (break_, width);
421        }
422
423        width = new_width;
424        char_i += 1;
425    }
426
427    let break_ = Break::new(text.len(), char_i, BreakType::End);
428    (break_, width)
429}
430
431/// Returns the next index at which the text will break by either:
432/// - A newline character.
433/// - A line wrap at the beginning of the whitespace that preceeds the first word
434/// exceeding the `max_width`.
435/// - A line wrap at the beginning of the first character exceeding the `max_width`,
436/// if no whitespace appears for `max_width` characters.
437///
438/// Also returns the width the line alongside the Break.
439fn next_break_by_whitespace(text: &str,
440                            font: &Font,
441                            font_size: f32,
442                            max_width: f32)
443                            -> (Break, f32) {
444    struct Last {
445        byte: usize,
446        char: usize,
447        width_before: f32,
448    }
449    let scale = super::pt_to_scale(font_size);
450    let mut last_whitespace_start = None;
451    let mut width = 0.0;
452    let mut char_i = 0;
453    let mut char_indices = text.char_indices().peekable();
454    let mut last_glyph = None;
455    while let Some((byte_i, ch)) = char_indices.next() {
456
457        // Check for a newline.
458        if ch == '\r' && peek_next_char(&mut char_indices, '\n') {
459            let break_ = Break::new(byte_i, char_i, BreakType::Newline { len_bytes: 2 });
460            return (break_, width);
461        } else if ch == '\n' {
462            let break_ = Break::new(byte_i, char_i, BreakType::Newline { len_bytes: 1 });
463            return (break_, width);
464        }
465
466        // Add the character's width to the width so far.
467        let new_width = width + advance_width(ch, font, scale, &mut last_glyph);
468
469        // Check for a line wrap.
470        if new_width > max_width {
471            match last_whitespace_start {
472                Some(Last { byte, char, width_before }) => {
473                    let break_ = Break::new(byte, char, BreakType::Wrap { len_bytes: 1 });
474                    return (break_, width_before);
475                }
476                None => {
477                    let break_ = Break::new(byte_i, char_i, BreakType::Wrap { len_bytes: 0 });
478                    return (break_, width);
479                }
480            }
481        }
482
483        // Check for a new whitespace.
484        if ch.is_whitespace() {
485            last_whitespace_start = Some(Last {
486                byte: byte_i,
487                char: char_i,
488                width_before: width,
489            });
490        }
491
492        width = new_width;
493        char_i += 1;
494    }
495
496    let break_ = Break::new(text.len(), char_i, BreakType::End);
497    (break_, width)
498}
499
500/// Produce the width of the given line of text including spaces (i.e. ' ').
501pub fn width(text: &str, font: &Font, font_size: f32) -> f32 {
502    let scale = Scale::uniform(font_size);
503    let point = rusttype::Point { x: 0.0, y: 0.0 };
504
505    let mut total_w = 0.0;
506    for g in font.layout(text, scale, point) {
507        match g.pixel_bounding_box() {
508            Some(bb) => total_w = bb.max.x as f32,
509            None => total_w += g.unpositioned().h_metrics().advance_width,
510        }
511    }
512
513    total_w
514}