Skip to main content

cranpose_text/
text_layout.rs

1use std::{
2    ops::Range,
3    sync::{
4        Arc,
5        atomic::{AtomicU32, Ordering},
6    },
7};
8
9use cranpose_ui_graphics::Rect;
10
11use crate::{
12    bidi_level::BidiLevel,
13    glyph_id::GlyphId,
14    glyph_ink::{InkReach, find_ink_past_lines, may_ink_past_lines},
15    index_range::{IndexRange, widen},
16    laid_glyphs::RunGlyphs,
17    layout_body::LayoutBody,
18    paragraph::TextAlign,
19    shaped_glyphs::GlyphOffset,
20    synthesized_style::SynthesizedStyle,
21    typeface::Typeface,
22};
23
24/// A paragraph laid out in lines: glyph runs placed for drawing, and the
25/// queries of Compose's `TextLayoutResult`. Distances are in pixels from the
26/// layout's top left corner; offsets are byte offsets in the text. The
27/// default is empty, ready for [`TextLayouter::layout_into`].
28///
29/// [`TextLayouter::layout_into`]: crate::TextLayouter::layout_into
30#[derive(Clone, Debug, Default)]
31pub struct TextLayout {
32    // The lines and glyphs, shared by the copies aligned at other widths
33    // while their lines keep their places.
34    body: Arc<LayoutBody>,
35    width: f32,
36    // The width the lines wrap at.
37    wrap: f32,
38    did_overflow_width: bool,
39    // Whether a glyph asks a bold or an italic of a face that lacks it.
40    // Kept beside the body, so the paint of a text that asks none does not
41    // load the body.
42    synthesizes: bool,
43    id: u32,
44    // How far the glyphs reach, as `glyph_reach` gives it. Kept beside the
45    // body, so a renderer reads it in the layout it holds.
46    glyph_reach: Rect,
47}
48
49impl PartialEq for TextLayout {
50    fn eq(&self, other: &Self) -> bool {
51        // Copies that share a body hold the same lines and glyphs. A text
52        // laid out again into the same lines draws the same, though it
53        // takes a new id.
54        (Arc::ptr_eq(&self.body, &other.body) || self.body == other.body)
55            && self.width == other.width
56            && self.did_overflow_width == other.did_overflow_width
57    }
58}
59
60// The id the next layout made or aligned takes.
61static NEXT_ID: AtomicU32 = AtomicU32::new(1);
62
63// A new id for a layout whose glyphs or positions changed.
64fn next_id() -> u32 {
65    NEXT_ID.fetch_add(1, Ordering::Relaxed).max(1)
66}
67
68// How far the visible text of the lines of `body` reaches across, from the
69// left edge of the leftmost to the right edge of the rightmost.
70fn lines_reach(body: &LayoutBody) -> (f32, f32) {
71    body.lines
72        .iter()
73        .map(|line| (line.left, line.right()))
74        .reduce(|(left, right), (line_left, line_right)| {
75            (left.min(line_left), right.max(line_right))
76        })
77        .unwrap_or_default()
78}
79
80// How far the glyphs of `body`, in the places its lines now take, reach, as
81// `TextLayout::glyph_reach` gives it; `reach` tells whether a glyph may put
82// ink past its advance or its typeface's lines. Inline: each layout takes
83// its lines.
84#[inline]
85fn glyph_reach(body: &LayoutBody, reach: InkReach) -> Rect {
86    let (left, right) = lines_reach(body);
87    let lines = Rect {
88        x: left,
89        y: 0.0,
90        width: right - left,
91        height: 0.0,
92    };
93    if !may_ink_past_lines(body, reach) {
94        return lines;
95    }
96    find_ink_past_lines(body, (left, right), reach).map_or(lines, |past| lines.union(past))
97}
98
99// Widths from `min` up to but not including `max`.
100#[derive(Clone, Copy, Debug, PartialEq)]
101pub(crate) struct WidthRange {
102    pub(crate) min: f32,
103    pub(crate) max: f32,
104}
105
106impl WidthRange {
107    pub(crate) const ALL: Self = Self {
108        min: f32::NEG_INFINITY,
109        max: f32::INFINITY,
110    };
111
112    // Narrows the range to the widths from `min` up to `max`.
113    pub(crate) fn narrow(&mut self, min: f32, max: f32) {
114        self.min = self.min.max(min);
115        self.max = self.max.min(max);
116    }
117}
118
119// A run of one typeface, size and bidi level inside a line, in visual order.
120#[derive(Clone, Debug, PartialEq)]
121pub(crate) struct LineRun {
122    pub(crate) text: IndexRange,
123    pub(crate) glyphs: IndexRange,
124    pub(crate) face: u32,
125    pub(crate) size: f32,
126    // Extra width of each cluster, half on each side.
127    pub(crate) spacing: f32,
128    // In a justified line, how many of the run's glyphs are spaces before
129    // the line's trailing whitespace and the extra width each of them takes.
130    pub(crate) spaces: u16,
131    pub(crate) word_spacing: f32,
132    // How far the run's baseline sits above the line's.
133    pub(crate) baseline_shift: f32,
134    pub(crate) level: BidiLevel,
135    // The left edge from the line's left edge and the width, before
136    // justification, and how far justification moves the run right.
137    pub(crate) x: f32,
138    pub(crate) width: f32,
139    pub(crate) shift: f32,
140}
141
142// One cluster of a run, left to right: the offset its text starts at, its
143// glyphs within the run and its width with the run's spacing.
144struct Cluster {
145    offset: usize,
146    glyphs: Range<usize>,
147    width: f32,
148}
149
150impl LineRun {
151    // The left edge from the line's left edge.
152    pub(crate) fn left(&self) -> f32 {
153        self.x + self.shift
154    }
155
156    // The right edge from the line's left edge.
157    fn right(&self) -> f32 {
158        self.left() + self.width + self.word_spacing * f32::from(self.spaces)
159    }
160
161    // Pixels per design unit of `face`, the run's typeface.
162    fn scale(&self, face: &Typeface) -> f32 {
163        self.size / f32::from(face.units_per_em())
164    }
165
166    // The run's clusters left to right, of its `glyphs` in its typeface
167    // `face`.
168    fn clusters<'a>(
169        &'a self,
170        glyphs: RunGlyphs<'a>,
171        face: &Typeface,
172    ) -> impl Iterator<Item = Cluster> + 'a {
173        let scale = self.scale(face);
174        let space = face.space();
175        let mut at = 0;
176        std::iter::from_fn(move || {
177            let first = glyphs.get(at)?;
178            let start = at;
179            let mut width = self.spacing;
180            while let Some(glyph) = glyphs
181                .get(at)
182                .filter(|glyph| glyph.cluster == first.cluster)
183            {
184                width += font_units(glyph.advance) * scale;
185                if space == Some(glyph.id()) {
186                    width += self.word_spacing;
187                }
188                at += 1;
189            }
190            Some(Cluster {
191                offset: first.text_offset(),
192                glyphs: start..at,
193                width,
194            })
195        })
196    }
197
198    // The run's clusters left to right, each with its left edge from the
199    // run's left edge and the offset where its text ends: where the next
200    // cluster in text order starts.
201    fn cluster_spans<'a>(
202        &'a self,
203        (glyphs, face): (RunGlyphs<'a>, &Typeface),
204    ) -> impl Iterator<Item = (f32, Cluster, usize)> + 'a {
205        let mut clusters = self.clusters(glyphs, face).peekable();
206        let mut left = 0.0;
207        let mut previous = None;
208        std::iter::from_fn(move || {
209            let cluster = clusters.next()?;
210            let end = if self.level.is_rtl() {
211                previous.unwrap_or_else(|| self.text.end())
212            } else {
213                clusters
214                    .peek()
215                    .map_or_else(|| self.text.end(), |next| next.offset)
216            };
217            previous = Some(cluster.offset);
218            let at = left;
219            left += cluster.width;
220            Some((at, cluster, end))
221        })
222    }
223}
224
225#[derive(Clone, Debug, PartialEq)]
226pub(crate) struct LayoutLine {
227    pub(crate) text: IndexRange,
228    // The end of the text before the line's trailing whitespace.
229    pub(crate) visible_end: u32,
230    pub(crate) runs: IndexRange,
231    pub(crate) top: f32,
232    pub(crate) baseline: f32,
233    pub(crate) bottom: f32,
234    // The left edge of the line's visible text, its indent, its width
235    // before justification and what justification adds to it.
236    pub(crate) left: f32,
237    pub(crate) indent: f32,
238    pub(crate) width: f32,
239    pub(crate) stretch: f32,
240    // The level of the paragraph the line is in.
241    pub(crate) level: BidiLevel,
242    pub(crate) ellipsized: bool,
243    pub(crate) ends_paragraph: bool,
244}
245
246impl LayoutLine {
247    // The right edge of the visible text.
248    pub(crate) fn right(&self) -> f32 {
249        self.left + self.width + self.stretch
250    }
251}
252
253/// Which of its two places a caret takes where the text before and after it
254/// runs in different directions.
255#[derive(Clone, Copy, Debug, PartialEq, Eq)]
256pub enum CaretDirection {
257    /// The place of the paragraph's direction: Compose's
258    /// `usePrimaryDirection = true`.
259    Primary,
260    /// The other place.
261    Secondary,
262}
263
264/// The glyphs of one run of a line, ready to draw.
265pub struct GlyphRun<'a> {
266    /// The typeface the glyphs come from.
267    pub typeface: &'a Typeface,
268    /// The font size in pixels.
269    pub size: f32,
270    pub(crate) glyphs: RunGlyphs<'a>,
271    pub(crate) offsets: Option<&'a [GlyphOffset]>,
272    pub(crate) run: &'a LineRun,
273    pub(crate) pen: f32,
274    pub(crate) baseline: f32,
275}
276
277/// A glyph placed in a layout.
278#[derive(Clone, Copy, Debug, PartialEq)]
279pub struct PositionedGlyph {
280    /// The glyph.
281    pub id: GlyphId,
282    /// Where its origin sits, from the layout's left edge.
283    pub x: f32,
284    /// Where its origin sits, from the layout's top edge.
285    pub y: f32,
286    /// The offset in the text of the character it draws, the first of its
287    /// cluster.
288    pub offset: usize,
289}
290
291impl GlyphRun<'_> {
292    /// Each glyph with the position of its origin in the layout, left to
293    /// right.
294    pub fn positions(&self) -> impl Iterator<Item = PositionedGlyph> + '_ {
295        let run = self.run;
296        let baseline = self.baseline - run.baseline_shift;
297        let scale = run.scale(self.typeface);
298        let mut pen = self.pen;
299        run.clusters(self.glyphs, self.typeface)
300            .flat_map(move |cluster| {
301                let mut x = pen + run.spacing / 2.0;
302                pen += cluster.width;
303                cluster.glyphs.filter_map(move |index| {
304                    let glyph = self.glyphs.get(index)?;
305                    let drawn = self
306                        .offsets
307                        .and_then(|offsets| offsets.get(index))
308                        .copied()
309                        .unwrap_or_default();
310                    let at = x + font_units(drawn.x) * scale;
311                    x += font_units(glyph.advance) * scale;
312                    Some(PositionedGlyph {
313                        id: glyph.id(),
314                        x: at,
315                        y: baseline - font_units(drawn.y) * scale,
316                        offset: cluster.offset,
317                    })
318                })
319            })
320    }
321
322    /// The text the run draws.
323    pub fn text(&self) -> Range<usize> {
324        self.run.text.range()
325    }
326
327    /// Where the run's baseline sits, from the layout's top edge.
328    pub fn baseline(&self) -> f32 {
329        self.baseline - self.run.baseline_shift
330    }
331
332    /// The run's left and right edges, from the layout's left edge.
333    pub fn edges(&self) -> (f32, f32) {
334        (self.pen, self.pen + self.run.right() - self.run.left())
335    }
336}
337
338impl TextLayout {
339    // The layout of `body`, whose lines wrap at `wrap`, laid out between
340    // `widths`, the narrowest and the widest it may be, of glyphs whose ink
341    // reaches as `reach` says that ask `synthesized` of their faces.
342    pub(crate) fn new(
343        mut body: LayoutBody,
344        wrap: f32,
345        widths: (f32, f32),
346        (reach, synthesized): (InkReach, SynthesizedStyle),
347    ) -> Self {
348        let width = body.width_between(widths.0, widths.1);
349        (body.ink_reach, body.synthesized) = (reach, synthesized);
350        let glyph_reach = glyph_reach(&body, reach);
351        Self {
352            did_overflow_width: body.widest > width || width > widths.1,
353            synthesizes: synthesized != SynthesizedStyle::default(),
354            body: Arc::new(body),
355            width,
356            wrap,
357            id: next_id(),
358            glyph_reach,
359        }
360    }
361
362    // Lays the layout out again between `widths`: `fill` fills its body, in
363    // the body's own storage when no copy shares it, and gives the width its
364    // lines wrap at, how the ink of its glyphs reaches and what they ask of
365    // their faces.
366    pub(crate) fn refill(
367        &mut self,
368        widths: (f32, f32),
369        fill: impl FnOnce(&mut LayoutBody) -> (f32, (InkReach, SynthesizedStyle)),
370    ) {
371        if Arc::get_mut(&mut self.body).is_none() {
372            self.body = Arc::default();
373        }
374        let body = Arc::make_mut(&mut self.body);
375        (self.wrap, (body.ink_reach, body.synthesized)) = fill(body);
376        self.synthesizes = body.synthesized != SynthesizedStyle::default();
377        self.width = body.width_between(widths.0, widths.1);
378        self.did_overflow_width = body.widest > self.width || self.width > widths.1;
379        self.glyph_reach = glyph_reach(body, body.ink_reach);
380        self.id = next_id();
381    }
382
383    // The body, which the layouter reads glyphs shaped before from.
384    pub(crate) fn body(&self) -> &LayoutBody {
385        &self.body
386    }
387
388    /// A number this layout's glyphs and positions go by: a layout laid out
389    /// or aligned again takes a new one, so a renderer keys what it drew of
390    /// a layout by it; a clone keeps it.
391    pub fn id(&self) -> u32 {
392        self.id
393    }
394
395    /// Whether the text laid out again between `min_width` and `max_width`,
396    /// with the rest of its input the same, breaks into the same lines:
397    /// [`TextLayout::align`] then gives that layout without laying the text
398    /// out again.
399    pub fn holds(&self, min_width: f32, max_width: f32) -> bool {
400        self.holds_wrap(self.body.wrap_width(min_width, max_width))
401    }
402
403    // Whether lines that wrap at `wrap` are this layout's: a hold ends where
404    // the comparisons of a fresh layout change, and a layout without one
405    // stands only for the width it wraps at.
406    fn holds_wrap(&self, wrap: f32) -> bool {
407        match self.body.hold {
408            Some(hold) => hold.min <= wrap && wrap < hold.max,
409            None => wrap.to_bits() == self.wrap.to_bits(),
410        }
411    }
412
413    /// The width the paragraph takes laid out between `min_width` and
414    /// `max_width`: its max intrinsic width rounded up to whole device
415    /// pixels within them, the max when they are equal, and its widest line
416    /// rounded up to whole device pixels within them when its lines wrap.
417    pub fn width_between(&self, min_width: f32, max_width: f32) -> f32 {
418        self.body.width_between(min_width, max_width)
419    }
420
421    /// The max widths, from `.0` up to and with `.1`, at which the paragraph
422    /// laid out from `min_width` keeps its width and its lines: every width
423    /// when its width follows no max, every width from its own when its text
424    /// fits, else the widths its lines wrap the same at below the width it
425    /// fits at, or only the one it wraps at when those are not known or its
426    /// widest line is wider than that one: its width is then the one it
427    /// wraps at, and moves with it.
428    pub fn max_widths_kept(&self, min_width: f32) -> (f32, f32) {
429        let body = &self.body;
430        if !body.follows_max {
431            (f32::NEG_INFINITY, f32::INFINITY)
432        } else if self.width >= body.content_width.max(min_width) {
433            (self.width, f32::INFINITY)
434        } else if let Some(hold) = body
435            .hold
436            .filter(|_| body.line_width.max(min_width) <= self.wrap)
437        {
438            let low = hold.min.max(self.width);
439            let high = hold.max.min(body.content_width).next_down();
440            if low <= high {
441                (low, high)
442            } else {
443                (self.wrap, self.wrap)
444            }
445        } else {
446            (self.wrap, self.wrap)
447        }
448    }
449
450    /// Places the lines across the width the paragraph takes between
451    /// `min_width` and `max_width` as `align` says, for widths the layout
452    /// [holds](TextLayout::holds).
453    pub fn align(&mut self, min_width: f32, max_width: f32, align: TextAlign) {
454        self.place(
455            self.body.wrap_width(min_width, max_width),
456            (min_width, max_width),
457            align,
458        );
459    }
460
461    /// [Aligns](TextLayout::align) the lines between `min_width` and
462    /// `max_width` when the layout [holds](TextLayout::holds) there; `false`,
463    /// leaving the layout as it was, when it does not.
464    pub fn try_align(&mut self, min_width: f32, max_width: f32, align: TextAlign) -> bool {
465        let wrap = self.body.wrap_width(min_width, max_width);
466        let held = self.holds_wrap(wrap);
467        if held {
468            self.place(wrap, (min_width, max_width), align);
469        }
470        held
471    }
472
473    // Places the lines, which wrap at `wrap`, across the width the
474    // paragraph takes between `(min_width, max_width)` as `align` says.
475    fn place(&mut self, wrap: f32, (min_width, max_width): (f32, f32), align: TextAlign) {
476        self.wrap = wrap;
477        self.width = self.body.width_at(wrap, min_width);
478        self.did_overflow_width = self.body.widest > self.width || self.width > max_width;
479        // Lines that keep their places keep the body the copies share, and
480        // the id what a renderer drew of the layout goes by.
481        if self.body.lines_move(self.width, align) {
482            self.move_lines(align);
483        }
484    }
485
486    // Places the lines across the layout's width as `align` says. Out of
487    // line: as a width animates, most lines keep their places, and a call
488    // that moves none then saves no registers.
489    #[inline(never)]
490    fn move_lines(&mut self, align: TextAlign) {
491        let body = Arc::make_mut(&mut self.body);
492        if body.align_lines(self.width, align) {
493            // Moved across, the glyphs and the lines' heights stay.
494            self.glyph_reach = glyph_reach(body, body.ink_reach);
495            self.id = next_id();
496        }
497    }
498
499    /// The paragraph's width: its max intrinsic width rounded up to whole
500    /// device pixels, within the widths it was laid out between.
501    pub fn width(&self) -> f32 {
502        self.width
503    }
504
505    /// The height of all lines.
506    pub fn height(&self) -> f32 {
507        self.body.height
508    }
509
510    /// The distance from the first line's baseline down to the next line's:
511    /// the first line's height before the paragraph's edges trim it.
512    pub fn line_advance(&self) -> f32 {
513        self.body.advance
514    }
515
516    /// The number of lines, at least one.
517    pub fn line_count(&self) -> usize {
518        self.body.lines.len()
519    }
520
521    /// How far the visible text of the lines reaches across: the left edge
522    /// of the leftmost line's and the right edge of the rightmost line's,
523    /// both zero for the empty default.
524    pub fn lines_reach(&self) -> (f32, f32) {
525        lines_reach(&self.body)
526    }
527
528    /// The typefaces the glyphs come from.
529    pub fn typefaces(&self) -> &[Typeface] {
530        &self.body.faces
531    }
532
533    /// The bold and the italic the glyphs ask of faces that lack them, which
534    /// a renderer synthesizes and which spread their ink.
535    pub fn synthesized_style(&self) -> SynthesizedStyle {
536        if self.synthesizes {
537            self.body.synthesized
538        } else {
539            SynthesizedStyle::default()
540        }
541    }
542
543    /// How far the glyphs reach, in pixels from the layout's top left
544    /// corner: across, the visible text of the lines and the ink a glyph puts
545    /// past it, as the overhang of an `f` that ends a line does; down, from
546    /// the top, or the ink above it, to the lowest ink, at the top when the
547    /// glyphs ink no lower than the box. A renderer joins it with the box it
548    /// draws the text in.
549    pub fn glyph_reach(&self) -> Rect {
550        self.glyph_reach
551    }
552
553    /// Whether the text had more lines than the line limit: Compose's
554    /// `didOverflowHeight` of a layout the line limit cut.
555    pub fn did_overflow_height(&self) -> bool {
556        self.body.did_overflow_height
557    }
558
559    /// Whether the paragraph is wider than the widest it was laid out at, or
560    /// a line wider than the paragraph: Compose's `didOverflowWidth`.
561    pub fn did_overflow_width(&self) -> bool {
562        self.did_overflow_width
563    }
564
565    /// Whether `line` shows an ellipsis in place of text.
566    pub fn is_line_ellipsized(&self, line: usize) -> bool {
567        self.body.lines[line].ellipsized
568    }
569
570    /// The offset where `line` starts. Panics when `line` is out of range, as
571    /// Compose throws.
572    pub fn line_start(&self, line: usize) -> usize {
573        self.body.lines[line].text.start()
574    }
575
576    /// The offset where `line` ends, after its line break and trailing
577    /// whitespace.
578    pub fn line_end(&self, line: usize) -> usize {
579        self.body.lines[line].text.end()
580    }
581
582    /// The offset where the visible text of `line` ends, before its line
583    /// break and trailing whitespace: Compose's `getLineEnd(line, true)`.
584    pub fn line_visible_end(&self, line: usize) -> usize {
585        widen(self.body.lines[line].visible_end)
586    }
587
588    /// The top of `line`.
589    pub fn line_top(&self, line: usize) -> f32 {
590        self.body.lines[line].top
591    }
592
593    /// The bottom of `line`.
594    pub fn line_bottom(&self, line: usize) -> f32 {
595        self.body.lines[line].bottom
596    }
597
598    /// The baseline of `line`.
599    pub fn line_baseline(&self, line: usize) -> f32 {
600        self.body.lines[line].baseline
601    }
602
603    /// The left edge of the visible text of `line`.
604    pub fn line_left(&self, line: usize) -> f32 {
605        self.body.lines[line].left
606    }
607
608    /// The right edge of the visible text of `line`.
609    pub fn line_right(&self, line: usize) -> f32 {
610        self.body.lines[line].right()
611    }
612
613    /// The line that holds `offset`; the last line for the end of the text.
614    pub fn line_for_offset(&self, offset: usize) -> usize {
615        self.body
616            .lines
617            .partition_point(|line| line.text.end() <= offset)
618            .min(self.body.lines.len() - 1)
619    }
620
621    /// The line at height `y`, the first above the text and the last below it.
622    pub fn line_for_vertical_position(&self, y: f32) -> usize {
623        self.body
624            .lines
625            .partition_point(|line| line.bottom <= y)
626            .min(self.body.lines.len() - 1)
627    }
628
629    /// The x of a caret at `offset`. Where the text before and after `offset`
630    /// runs in different directions the caret has two places, and `direction`
631    /// picks one.
632    pub fn horizontal_position(&self, offset: usize, direction: CaretDirection) -> f32 {
633        let line = &self.body.lines[self.line_for_offset(offset)];
634        let runs = &self.body.runs[line.runs.range()];
635        let after = runs
636            .iter()
637            .find(|run| run.text.start() <= offset && offset < run.text.end());
638        let before = runs
639            .iter()
640            .find(|run| run.text.start() < offset && offset <= run.text.end());
641        let run = match (before, after) {
642            (Some(before), Some(after)) if before.level != after.level => {
643                if (before.level == line.level) == (direction == CaretDirection::Primary) {
644                    before
645                } else {
646                    after
647                }
648            }
649            (_, Some(run)) | (Some(run), None) => run,
650            (None, None) => {
651                return if line.level.is_rtl() {
652                    line.right()
653                } else {
654                    line.left
655                };
656            }
657        };
658        line.left + self.caret_in_run(run, offset)
659    }
660
661    /// The offset of the caret nearest to the point `x`, `y`.
662    pub fn offset_for_position(&self, x: f32, y: f32) -> usize {
663        let line = &self.body.lines[self.line_for_vertical_position(y)];
664        let runs = &self.body.runs[line.runs.range()];
665        let local = x - line.left;
666        let Some(run) = runs
667            .iter()
668            .find(|run| local < run.right())
669            .or_else(|| runs.last())
670        else {
671            return line.text.start();
672        };
673        self.offset_in_run(run, local - run.left())
674    }
675
676    /// The glyph runs of `line`, left to right.
677    pub fn glyph_runs(&self, line: usize) -> impl Iterator<Item = GlyphRun<'_>> + '_ {
678        self.body.glyph_runs(line)
679    }
680
681    /// The box of the character at `offset`, across its line: Compose's
682    /// `getBoundingBox`. A zero-width box at the caret for an offset with no
683    /// character, such as the end of the text.
684    pub fn bounding_box(&self, offset: usize) -> Rect {
685        let line = &self.body.lines[self.line_for_offset(offset)];
686        let found = self.body.runs[line.runs.range()].iter().find_map(|run| {
687            run.cluster_spans(self.body.run_glyphs(run))
688                .find(|(_, cluster, end)| (cluster.offset..*end).contains(&offset))
689                .map(|(left, cluster, _)| (run.left() + left, cluster.width))
690        });
691        let (x, width) = found.unwrap_or_else(|| {
692            (
693                self.horizontal_position(offset, CaretDirection::Primary) - line.left,
694                0.0,
695            )
696        });
697        Rect {
698            x: line.left + x,
699            y: line.top,
700            width,
701            height: line.bottom - line.top,
702        }
703    }
704
705    /// The caret at `offset` as a zero-width rectangle across its line:
706    /// Compose's `getCursorRect`.
707    pub fn cursor_rect(&self, offset: usize) -> Rect {
708        let line = &self.body.lines[self.line_for_offset(offset)];
709        Rect {
710            x: self.horizontal_position(offset, CaretDirection::Primary),
711            y: line.top,
712            width: 0.0,
713            height: line.bottom - line.top,
714        }
715    }
716
717    /// Pushes onto `rects` the rectangles that cover the text from `start`
718    /// to `end` line by line, one for each stretch of it that runs in one
719    /// direction, within the paragraph's width or its visible text: Compose's
720    /// `getPathForRange` as rectangles. The spaces a wrapped line ends with
721    /// hang past the widest line, which they do not widen, and cover nothing
722    /// there.
723    pub fn selection_rects(&self, start: usize, end: usize, rects: &mut Vec<Rect>) {
724        let width = self.width;
725        for line in &self.body.lines {
726            if line.text.end() <= start || line.text.start() >= end {
727                continue;
728            }
729            let mut open: Option<(f32, f32)> = None;
730            for run in &self.body.runs[line.runs.range()] {
731                for (left, cluster, cluster_end) in run.cluster_spans(self.body.run_glyphs(run)) {
732                    let x = run.left() + left;
733                    let covered = cluster.offset < end && cluster_end > start;
734                    match (covered, open) {
735                        (true, Some((from, _))) => open = Some((from, x + cluster.width)),
736                        (true, None) => open = Some((x, x + cluster.width)),
737                        (false, Some(span)) => {
738                            push_span(rects, (line, width), span);
739                            open = None;
740                        }
741                        (false, None) => {}
742                    }
743                }
744            }
745            if let Some(span) = open {
746                push_span(rects, (line, width), span);
747            }
748        }
749    }
750
751    // The caret x of `offset` inside `run`, from the line's left edge.
752    fn caret_in_run(&self, run: &LineRun, offset: usize) -> f32 {
753        let (glyphs, face) = self.body.run_glyphs(run);
754        let before: f32 = run
755            .clusters(glyphs, face)
756            .filter(|cluster| cluster.offset < offset)
757            .map(|cluster| cluster.width)
758            .sum();
759        if run.level.is_rtl() {
760            run.right() - before
761        } else {
762            run.left() + before
763        }
764    }
765
766    // The offset nearest to `x` pixels from the left edge of `run`: each
767    // cluster's left half gives the offset on its left.
768    fn offset_in_run(&self, run: &LineRun, x: f32) -> usize {
769        for (left, cluster, end) in run.cluster_spans(self.body.run_glyphs(run)) {
770            if x < left + cluster.width / 2.0 {
771                return if run.level.is_rtl() {
772                    end
773                } else {
774                    cluster.offset
775                };
776            }
777        }
778        if run.level.is_rtl() {
779            run.text.start()
780        } else {
781            run.text.end()
782        }
783    }
784}
785
786// Adds the rectangle of `line` from `left` to `right`, from the line's left
787// edge.
788// Pushes the rectangle of `line` from `left` to `right` within the
789// paragraph's `width`, or past it as far as the line's visible text reaches.
790fn push_span(rects: &mut Vec<Rect>, (line, width): (&LayoutLine, f32), (left, right): (f32, f32)) {
791    let (low, high) = (line.left.min(0.0), width.max(line.left + line.width));
792    let (left, right) = (
793        (line.left + left).clamp(low, high),
794        (line.left + right).clamp(low, high),
795    );
796    if left < right {
797        rects.push(Rect {
798            x: left,
799            y: line.top,
800            width: right - left,
801            height: line.bottom - line.top,
802        });
803    }
804}
805
806// A distance in font design units as a float, exact for every value under
807// 2^24: shaped values lie far inside it for any font, whose em is at most
808// 16,384 units.
809pub(crate) fn font_units(value: i32) -> f32 {
810    value as f32
811}