limn_text_layout/
glyph.rs

1/// Logic and types specific to individual glyph layout.
2
3use super::Font;
4use types::{Range, Rect, RectExt};
5use std;
6use rusttype;
7use rusttype::LayoutIter;
8use super::line::LineInfo;
9
10/// An iterator yielding the `Rect` for each `char`'s `Glyph` in the given `text`.
11pub struct GlyphRects<'a, 'b> {
12    /// The *y* axis `Range` of the `Line` for which character `Rect`s are being yielded.
13    ///
14    /// Every yielded `Rect` will use this as its `y` `Range`.
15    y: Range,
16    /// The position of the next `Rect`'s left edge along the *x* axis.
17    next_left: f32,
18    /// `PositionedGlyphs` yielded by the RustType `LayoutIter`.
19    layout: LayoutIter<'a, 'b>,
20}
21
22
23impl<'a, 'b> Iterator for GlyphRects<'a, 'b> {
24    type Item = Rect;
25    fn next(&mut self) -> Option<Self::Item> {
26        let GlyphRects { ref mut next_left, ref mut layout, y } = *self;
27        layout.next().map(|g| {
28            let left = *next_left;
29            let right = g.pixel_bounding_box()
30                .map(|bb| bb.max.x as f32)
31                .unwrap_or_else(|| left + g.unpositioned().h_metrics().advance_width);
32            *next_left = right;
33            let x = Range::new(left, right);
34            Rect::from_ranges(x, y)
35        })
36    }
37}
38
39/// An iterator that, for every `(line, line_rect)` pair yielded by the given iterator,
40/// produces an iterator that yields a `Rect` for every character in that line.
41pub struct GlyphRectsPerLine<'a, I> {
42    lines_with_rects: I,
43    font: &'a Font,
44    font_size: f32,
45}
46
47impl<'a, I> GlyphRectsPerLine<'a, I>
48    where I: Iterator<Item = (&'a str, Rect)>
49{
50    /// Produce an iterator that, for every `(line, line_rect)` pair yielded by the given iterator,
51    /// produces an iterator that yields a `Rect` for every character in that line.
52    ///
53    /// This is useful when information about character positioning is needed when reasoning about
54    /// text layout.
55    pub fn new(lines_with_rects: I, font: &'a Font, font_size: f32) -> GlyphRectsPerLine<'a, I> {
56        GlyphRectsPerLine {
57            lines_with_rects: lines_with_rects,
58            font: font,
59            font_size: font_size,
60        }
61    }
62}
63impl<'a, I> Iterator for GlyphRectsPerLine<'a, I>
64    where I: Iterator<Item = (&'a str, Rect)>
65{
66    type Item = GlyphRects<'a, 'a>;
67    fn next(&mut self) -> Option<Self::Item> {
68        let GlyphRectsPerLine { ref mut lines_with_rects, font, font_size } = *self;
69        let scale = super::pt_to_scale(font_size);
70        lines_with_rects.next().map(|(line_text, line_rect)| {
71            let (x, y) = (line_rect.left(), line_rect.top());
72            let point = rusttype::Point { x: x, y: y };
73            GlyphRects {
74                next_left: line_rect.left(),
75                layout: font.layout(line_text, scale, point),
76                y: line_rect.y_range(),
77            }
78        })
79    }
80}
81
82/// Yields a `Rect` for each selected character in a single line of text.
83///
84/// This iterator can only be produced by the `SelectedCharRectsPerLine` iterator.
85pub struct SelectedGlyphRects<'a, 'b> {
86    enumerated_rects: std::iter::Enumerate<GlyphRects<'a, 'b>>,
87    end_char_idx: usize,
88}
89impl<'a, 'b> Iterator for SelectedGlyphRects<'a, 'b> {
90    type Item = Rect;
91    fn next(&mut self) -> Option<Self::Item> {
92        let SelectedGlyphRects { ref mut enumerated_rects, end_char_idx } = *self;
93        enumerated_rects.next()
94            .and_then(|(i, rect)| if i < end_char_idx { Some(rect) } else { None })
95    }
96}
97
98
99/// Yields an iteraor yielding `Rect`s for each selected character in each line of text within
100/// the given iterator yielding char `Rect`s.
101///
102/// Given some `start` and `end` indices, only `Rect`s for `char`s between these two indices
103/// will be produced.
104///
105/// All lines that have no selected `Rect`s will be skipped.
106pub struct SelectedGlyphRectsPerLine<'a, I> {
107    enumerated_rects_per_line: std::iter::Enumerate<GlyphRectsPerLine<'a, I>>,
108    start_cursor_idx: super::cursor::Index,
109    end_cursor_idx: super::cursor::Index,
110}
111
112impl<'a, I> SelectedGlyphRectsPerLine<'a, I>
113    where I: Iterator<Item = (&'a str, Rect)>
114{
115    /// Produces an iterator that yields iteraors yielding `Rect`s for each selected character in
116    /// each line of text within the given iterator yielding char `Rect`s.
117    ///
118    /// Given some `start` and `end` indices, only `Rect`s for `char`s between these two indices
119    /// will be produced.
120    ///
121    /// All lines that have no selected `Rect`s will be skipped.
122    pub fn new(lines_with_rects: I,
123               font: &'a Font,
124               font_size: f32,
125               start: super::cursor::Index,
126               end: super::cursor::Index)
127               -> SelectedGlyphRectsPerLine<'a, I> {
128        SelectedGlyphRectsPerLine {
129            enumerated_rects_per_line: GlyphRectsPerLine::new(lines_with_rects, font, font_size)
130                .enumerate(),
131            start_cursor_idx: start,
132            end_cursor_idx: end,
133        }
134    }
135}
136impl<'a, I> Iterator for SelectedGlyphRectsPerLine<'a, I>
137    where I: Iterator<Item = (&'a str, Rect)>
138{
139    type Item = SelectedGlyphRects<'a, 'a>;
140    fn next(&mut self) -> Option<Self::Item> {
141        let SelectedGlyphRectsPerLine { ref mut enumerated_rects_per_line,
142                                        start_cursor_idx,
143                                        end_cursor_idx } = *self;
144
145        enumerated_rects_per_line.next().map(|(i, rects)| {
146            let end_char_idx =
147                    // If this is the last line, the end is the char after the final selected char.
148                    if i == end_cursor_idx.line {
149                        end_cursor_idx.char
150                    // Otherwise if in range, every char in the line is selected.
151                    } else if start_cursor_idx.line <= i && i < end_cursor_idx.line {
152                        std::u32::MAX as usize
153                    // Otherwise if out of range, no chars are selected.
154                    } else {
155                        0
156                    };
157
158            let mut enumerated_rects = rects.enumerate();
159
160            // If this is the first line, skip all non-selected chars.
161            if i == start_cursor_idx.line {
162                for _ in 0..start_cursor_idx.char {
163                    enumerated_rects.next();
164                }
165            }
166
167            SelectedGlyphRects {
168                enumerated_rects: enumerated_rects,
169                end_char_idx: end_char_idx,
170            }
171        })
172    }
173}
174
175
176/// Find the index of the character that directly follows the cursor at the given `cursor_idx`.
177///
178/// Returns `None` if either the given `cursor::Index` `line` or `idx` fields are out of bounds
179/// of the line information yielded by the `line_infos` iterator.
180pub fn index_after_cursor<I>(mut line_infos: I, cursor_idx: super::cursor::Index) -> Option<usize>
181    where I: Iterator<Item = LineInfo>
182{
183    line_infos.nth(cursor_idx.line)
184        .and_then(|line_info| {
185            let start_char = line_info.start_char;
186            let end_char = line_info.end_char();
187            let char_index = start_char + cursor_idx.char;
188            if char_index <= end_char {
189                Some(char_index)
190            } else {
191                None
192            }
193        })
194}