Skip to main content

zng_ext_font/
shaping.rs

1use std::{
2    cmp, fmt,
3    hash::{BuildHasher, Hash},
4    mem, ops,
5};
6
7use zng_app::widget::info::InlineSegmentInfo;
8use zng_ext_image::{ColorType, IMAGES, ImageDataFormat, ImageOptions, ImageSource, ImageVar};
9use zng_ext_l10n::{Lang, lang};
10use zng_layout::{
11    context::{InlineConstraintsLayout, InlineConstraintsMeasure, InlineSegmentPos, LayoutDirection, TextSegmentKind},
12    unit::{Align, Factor2d, FactorUnits, Px, PxBox, PxConstraints2d, PxPoint, PxRect, PxSize, about_eq, euclid},
13};
14use zng_txt::Txt;
15use zng_view_api::font::{GlyphIndex, GlyphInstance};
16
17use crate::{
18    BidiLevel, CaretIndex, Font, FontList, HYPHENATION, Hyphens, Justify, LineBreak, ParagraphBreak, SegmentedText, TextSegment, WordBreak,
19    font_features::RFontFeatures,
20};
21
22/// Reasons why a font might fail to load a glyph.
23#[derive(Clone, Copy, PartialEq, Debug)]
24#[non_exhaustive]
25pub enum GlyphLoadingError {
26    /// The font didn't contain a glyph with that ID.
27    NoSuchGlyph,
28    /// A platform function returned an error.
29    PlatformError,
30}
31impl std::error::Error for GlyphLoadingError {}
32impl fmt::Display for GlyphLoadingError {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        use GlyphLoadingError::*;
35        match self {
36            NoSuchGlyph => write!(f, "no such glyph"),
37            PlatformError => write!(f, "platform error"),
38        }
39    }
40}
41
42/// Extra configuration for [`shape_text`](Font::shape_text).
43#[derive(Debug, Clone)]
44#[non_exhaustive]
45pub struct TextShapingArgs {
46    /// Extra spacing to add after each character.
47    pub letter_spacing: Px,
48
49    /// Extra spacing to add after each space (U+0020 SPACE).
50    pub word_spacing: Px,
51
52    /// Height of each line.
53    ///
54    /// Default can be computed using [`FontMetrics::line_height`].
55    ///
56    /// [`FontMetrics::line_height`]: crate::FontMetrics::line_height
57    pub line_height: Px,
58
59    /// Extra spacing added in between lines.
60    pub line_spacing: Px,
61
62    /// Replacement space between paragraphs.
63    pub paragraph_spacing: Px,
64
65    /// Extra spacing in paragraph lines start.
66    ///
67    /// The `bool` indicates if spacing is inverted (hang), applied to all paragraph lines except the first.
68    pub paragraph_indent: (Px, bool),
69
70    /// Primary language of the text.
71    pub lang: Lang,
72
73    /// Text flow direction.
74    pub direction: LayoutDirection,
75
76    /// Don't use font ligatures.
77    pub ignore_ligatures: bool,
78
79    /// Don't use font letter spacing.
80    pub disable_kerning: bool,
81
82    /// Width of the TAB character.
83    pub tab_x_advance: Px,
84
85    /// Inline constraints for initial text shaping and wrap.
86    pub inline_constraints: Option<InlineConstraintsMeasure>,
87
88    /// Finalized font features.
89    pub font_features: RFontFeatures,
90
91    /// Maximum line width.
92    ///
93    /// Is `Px::MAX` when text wrap is disabled.
94    pub max_width: Px,
95
96    /// Definition of paragraphs.
97    pub paragraph_break: ParagraphBreak,
98
99    /// Line break config for Chinese, Japanese, or Korean text.
100    pub line_break: LineBreak,
101
102    /// World break config.
103    ///
104    /// This value is only considered if it is impossible to fit the word to a line.
105    pub word_break: WordBreak,
106
107    /// Hyphen breaks config.
108    pub hyphens: Hyphens,
109
110    /// Character rendered when text is hyphenated by break.
111    pub hyphen_char: Txt,
112
113    /// Obscure the text with the replacement char.
114    pub obscuring_char: Option<char>,
115}
116impl Default for TextShapingArgs {
117    fn default() -> Self {
118        TextShapingArgs {
119            letter_spacing: Px(0),
120            word_spacing: Px(0),
121            line_height: Px(0),
122            line_spacing: Px(0),
123            paragraph_spacing: Px(0),
124            paragraph_indent: (Px(0), false),
125            lang: lang!(und),
126            direction: LayoutDirection::LTR,
127            ignore_ligatures: false,
128            disable_kerning: false,
129            tab_x_advance: Px(0),
130            inline_constraints: None,
131            font_features: RFontFeatures::default(),
132            max_width: Px::MAX,
133            paragraph_break: Default::default(),
134            line_break: Default::default(),
135            word_break: Default::default(),
136            hyphens: Default::default(),
137            hyphen_char: Txt::from_char('-'),
138            obscuring_char: None,
139        }
140    }
141}
142
143/// Configuration for [`ShapedText::reshape_lines`].
144#[derive(Debug, Clone, Default)]
145#[non_exhaustive]
146pub struct TextReshapingArgs {
147    /// Layout constraints.
148    pub constraints: PxConstraints2d,
149    /// Inline layout constraints.
150    pub inline_constraints: Option<InlineConstraintsLayout>,
151    /// Text alignment inside the available space.
152    pub align: Align,
153    /// Text alignment inside the available space when it overflows.
154    pub overflow_align: Align,
155    /// Text flow direction.
156    pub direction: LayoutDirection,
157
158    /// Line height.
159    pub line_height: Px,
160    /// Spacing between lines of the same paragraph.
161    pub line_spacing: Px,
162    /// Spacing between paragraphs.
163    pub paragraph_spacing: Px,
164    /// Extra spacing in paragraph lines start.
165    ///
166    /// The `bool` indicates if spacing is inverted (hang), applied to all paragraph lines except the first.
167    pub paragraph_indent: (Px, bool),
168    /// How paragraphs are defined.
169    pub paragraph_break: ParagraphBreak,
170}
171
172/// Defines a range of segments in a [`ShapedText`] that form a line.
173#[derive(Debug, Clone, Copy, PartialEq)]
174struct LineRange {
175    /// Exclusive segment index, is the `segments.len()` for the last line and the index of the first
176    /// segment after the line break for other lines.
177    end: usize,
178    /// Pixel width of the line.
179    width: f32,
180    /// Applied align offset to the right.
181    x_offset: f32,
182    directions: LayoutDirections,
183}
184
185/// Defines the font of a range of glyphs in a [`ShapedText`].
186#[derive(Clone)]
187struct FontRange {
188    font: Font,
189    /// Exclusive glyph range end.
190    end: usize,
191}
192impl PartialEq for FontRange {
193    fn eq(&self, other: &Self) -> bool {
194        self.font == other.font && self.end == other.end
195    }
196}
197impl fmt::Debug for FontRange {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        f.debug_struct("FontInfo")
200            .field("font", &self.font.face().display_name().name())
201            .field("end", &self.end)
202            .finish()
203    }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq)]
207struct GlyphSegment {
208    pub text: TextSegment,
209    /// glyph exclusive end.
210    pub end: usize,
211    /// Segment offset in the line.
212    pub x: f32,
213    /// Advance/width of segment.
214    pub advance: f32,
215}
216
217/// `Vec<GlyphSegment>` with helper methods.
218#[derive(Debug, Default, Clone, PartialEq)]
219struct GlyphSegmentVec(Vec<GlyphSegment>);
220impl GlyphSegmentVec {
221    /// Exclusive glyphs range of the segment.
222    fn glyphs(&self, index: usize) -> IndexRange {
223        let start = if index == 0 { 0 } else { self.0[index - 1].end };
224        let end = self.0[index].end;
225        IndexRange(start, end)
226    }
227
228    /// Exclusive glyphs range from an exclusive range of segments.
229    fn glyphs_range(&self, range: IndexRange) -> IndexRange {
230        let IndexRange(start, end) = range;
231
232        if end == 0 {
233            return IndexRange(0, 0);
234        }
235
236        let start = if start == 0 { 0 } else { self.0[start - 1].end };
237        let end = self.0[end - 1].end;
238
239        IndexRange(start, end)
240    }
241}
242
243/// `Vec<LineRange>` with helper methods.
244#[derive(Debug, Default, Clone, PartialEq)]
245struct LineRangeVec(Vec<LineRange>);
246impl LineRangeVec {
247    /// Exclusive segments range of the line.
248    fn segs(&self, index: usize) -> IndexRange {
249        let end = self.0[index].end;
250        let start = if index == 0 { 0 } else { self.0[index - 1].end };
251        IndexRange(start, end)
252    }
253
254    /// Line width.
255    fn width(&self, index: usize) -> f32 {
256        self.0[index].width
257    }
258
259    /// Line x offset.
260    fn x_offset(&self, index: usize) -> f32 {
261        self.0[index].x_offset
262    }
263
264    /// Iter segment ranges.
265    fn iter_segs(&self) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
266        self.iter_segs_skip(0)
267    }
268
269    /// Iter segment ranges starting at a line.
270    fn iter_segs_skip(&self, start_line: usize) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
271        let mut start = self.segs(start_line).start();
272        self.0[start_line..].iter().map(move |l| {
273            let r = IndexRange(start, l.end);
274            start = l.end;
275            (l.width, r)
276        })
277    }
278
279    /// Returns `true` if there is more then one line.
280    fn is_multi(&self) -> bool {
281        self.0.len() > 1
282    }
283
284    fn first_mut(&mut self) -> &mut LineRange {
285        &mut self.0[0]
286    }
287
288    fn last(&self) -> LineRange {
289        self.0[self.0.len() - 1]
290    }
291
292    fn last_mut(&mut self) -> &mut LineRange {
293        let l = self.0.len() - 1;
294        &mut self.0[l]
295    }
296}
297
298/// `Vec<FontRange>` with helper methods.
299#[derive(Debug, Default, Clone, PartialEq)]
300struct FontRangeVec(Vec<FontRange>);
301impl FontRangeVec {
302    /// Iter glyph ranges.
303    fn iter_glyphs(&self) -> impl Iterator<Item = (&Font, IndexRange)> + '_ {
304        let mut start = 0;
305        self.0.iter().map(move |f| {
306            let r = IndexRange(start, f.end);
307            start = f.end;
308            (&f.font, r)
309        })
310    }
311
312    /// Iter glyph ranges clipped by `glyphs_range`.
313    fn iter_glyphs_clip(&self, glyphs_range: IndexRange) -> impl Iterator<Item = (&Font, IndexRange)> + '_ {
314        let mut start = glyphs_range.start();
315        let end = glyphs_range.end();
316        let first_font = self.0.iter().position(|f| f.end > start).unwrap_or(self.0.len().saturating_sub(1));
317
318        self.0[first_font..].iter().map_while(move |f| {
319            let i = f.end.min(end);
320
321            if i > start {
322                let r = IndexRange(start, i);
323                start = i;
324                Some((&f.font, r))
325            } else {
326                None
327            }
328        })
329    }
330
331    /// Returns a reference to the font.
332    fn font(&self, index: usize) -> &Font {
333        &self.0[index].font
334    }
335}
336
337#[derive(Clone)]
338struct GlyphImage(ImageVar);
339impl PartialEq for GlyphImage {
340    fn eq(&self, other: &Self) -> bool {
341        self.0.var_eq(&other.0)
342    }
343}
344impl fmt::Debug for GlyphImage {
345    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346        write!(f, "GlyphImage(_)")
347    }
348}
349
350/// Output of [text layout].
351///
352/// [text layout]: Font::shape_text
353#[derive(Debug, Clone, PartialEq)]
354pub struct ShapedText {
355    // glyphs are in text order by segments and in visual (LTR) order within segments.
356    glyphs: Vec<GlyphInstance>,
357    // char byte index of each glyph in the segment that covers it.
358    clusters: Vec<u32>,
359    // segments of `glyphs` and `clusters`.
360    segments: GlyphSegmentVec,
361    lines: LineRangeVec,
362    fonts: FontRangeVec,
363    // sorted map of `glyphs` index -> image.
364    images: Vec<(u32, GlyphImage)>,
365
366    line_height: Px,
367    line_spacing: Px,
368    paragraph_spacing: Px,
369    paragraph_break: ParagraphBreak,
370
371    orig_line_height: Px,
372    orig_line_spacing: Px,
373    orig_first_line: PxSize,
374    orig_last_line: PxSize,
375
376    // offsets from the line_height bottom
377    baseline: Px,
378    overline: Px,
379    strikethrough: Px,
380    underline: Px,
381    underline_descent: Px,
382
383    /// vertical align offset applied.
384    mid_offset: f32,
385    align_size: PxSize,
386    align: Align,
387    justify: Justify,    // applied justify if `align` is FILL_X
388    justified: Vec<f32>, // each line has up to 3 values here, depending on first/last segs are trimmed
389    overflow_align: Align,
390    direction: LayoutDirection,
391
392    // inline layout values
393    is_inlined: bool,
394    first_wrapped: bool,
395    first_line: PxRect,
396    mid_clear: Px,
397    mid_size: PxSize,
398    last_line: PxRect,
399
400    has_colored_glyphs: bool,
401}
402
403/// Represents normal and colored glyphs in [`ShapedText::colored_glyphs`].
404pub enum ShapedColoredGlyphs<'a> {
405    /// Sequence of not colored glyphs, use the base color to fill.
406    Normal(&'a [GlyphInstance]),
407    /// Colored glyph.
408    Colored {
409        /// Point that must be used for all `glyphs`.
410        point: euclid::Point2D<f32, Px>,
411        /// The glyph that is replaced by `glyphs`.
412        ///
413        /// Must be used as fallback if any `glyphs` cannot be rendered.
414        base_glyph: GlyphIndex,
415
416        /// The colored glyph components.
417        glyphs: super::ColorGlyph<'a>,
418    },
419}
420
421/// Represents normal and image glyphs in [`ShapedText::image_glyphs`].
422pub enum ShapedImageGlyphs<'a> {
423    /// Sequence of not image glyphs.
424    Normal(&'a [GlyphInstance]),
425    /// Image glyph.
426    Image {
427        /// Origin and size of the image in the shaped text.
428        ///
429        /// The size is empty is the image has not loaded yet.
430        rect: euclid::Rect<f32, Px>,
431        /// The glyph that is replaced by `img`.
432        ///
433        /// Must be used as fallback if the `img` cannot be rendered.
434        base_glyph: GlyphIndex,
435        /// The image.
436        img: &'a ImageVar,
437    },
438}
439
440impl ShapedText {
441    /// New empty text.
442    pub fn new(font: &Font) -> Self {
443        font.shape_text(&SegmentedText::new("", LayoutDirection::LTR), &TextShapingArgs::default())
444    }
445
446    /// Glyphs by font.
447    ///
448    /// The glyphs are in text order by segments and in visual order (LTR) within segments, so
449    /// the RTL text "لما " will have the space glyph first, then "’álif", "miim", "láam".
450    ///
451    /// All glyph points are set as offsets to the top-left of the text full text.
452    ///
453    /// Note that multiple glyphs can map to the same char and multiple chars can map to the same glyph.
454    pub fn glyphs(&self) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
455        self.fonts.iter_glyphs().map(move |(f, r)| (f, &self.glyphs[r.iter()]))
456    }
457
458    /// Glyphs in a range by font.
459    ///
460    /// Similar output to [`glyphs`], but only glyphs in the `range`.
461    ///
462    /// [`glyphs`]: Self::glyphs
463    pub fn glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
464        self.glyphs_slice_impl(IndexRange::from_bounds(range))
465    }
466    fn glyphs_slice_impl(&self, range: IndexRange) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
467        self.fonts.iter_glyphs_clip(range).map(move |(f, r)| (f, &self.glyphs[r.iter()]))
468    }
469
470    /// If the shaped text has any Emoji glyph associated with a font that has color palettes.
471    pub fn has_colored_glyphs(&self) -> bool {
472        self.has_colored_glyphs
473    }
474
475    /// If the shaped text has any Emoji glyph associated with a pixel image.
476    pub fn has_images(&self) -> bool {
477        !self.images.is_empty()
478    }
479
480    /// Glyphs by font and palette color.
481    pub fn colored_glyphs(&self) -> impl Iterator<Item = (&Font, ShapedColoredGlyphs<'_>)> {
482        ColoredGlyphsIter {
483            glyphs: self.glyphs(),
484            maybe_colored: None,
485        }
486    }
487
488    /// Glyphs in a range by font and palette color.
489    pub fn colored_glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, ShapedColoredGlyphs<'_>)> {
490        ColoredGlyphsIter {
491            glyphs: self.glyphs_slice_impl(IndexRange::from_bounds(range)),
492            maybe_colored: None,
493        }
494    }
495
496    /// Glyphs by font and associated image.
497    pub fn image_glyphs(&self) -> impl Iterator<Item = (&Font, ShapedImageGlyphs<'_>)> {
498        ImageGlyphsIter {
499            glyphs: self.glyphs(),
500            glyphs_i: 0,
501            images: &self.images,
502            maybe_img: None,
503        }
504    }
505
506    /// Glyphs in a range by font and associated image.
507    pub fn image_glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, ShapedImageGlyphs<'_>)> {
508        let range = IndexRange::from_bounds(range);
509        ImageGlyphsIter {
510            glyphs_i: range.start() as _,
511            glyphs: self.glyphs_slice_impl(range),
512            images: &self.images,
513            maybe_img: None,
514        }
515    }
516
517    /// Glyphs by font in the range.
518    fn glyphs_range(&self, range: IndexRange) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
519        self.fonts.iter_glyphs_clip(range).map(|(f, r)| (f, &self.glyphs[r.iter()]))
520    }
521
522    /// Index of each char byte in the segment range.
523    /// The first char in the segment is 0.
524    fn clusters_range(&self, range: IndexRange) -> &[u32] {
525        &self.clusters[range.iter()]
526    }
527
528    fn seg_glyphs_with_x_advance(
529        &self,
530        seg_idx: usize,
531        glyphs_range: IndexRange,
532    ) -> impl Iterator<Item = (&Font, impl Iterator<Item = (GlyphInstance, f32)> + '_)> + '_ {
533        let mut gi = glyphs_range.start();
534        let seg_x = if gi < self.glyphs.len() { self.glyphs[gi].point.x } else { 0.0 };
535        let seg_advance = self.segments.0[seg_idx].advance;
536        self.glyphs_range(glyphs_range).map(move |(font, glyphs)| {
537            let g_adv = glyphs.iter().map(move |g| {
538                gi += 1;
539
540                let adv = if gi == glyphs_range.end() {
541                    (seg_x + seg_advance) - g.point.x
542                } else {
543                    self.glyphs[gi].point.x - g.point.x
544                };
545                (*g, adv)
546            });
547
548            (font, g_adv)
549        })
550    }
551
552    fn seg_cluster_glyphs_with_x_advance(
553        &self,
554        seg_idx: usize,
555        glyphs_range: IndexRange,
556    ) -> impl Iterator<Item = (&Font, impl Iterator<Item = (u32, &[GlyphInstance], f32)>)> {
557        let mut gi = glyphs_range.start();
558        let seg_x = if gi < self.glyphs.len() { self.glyphs[gi].point.x } else { 0.0 };
559        let seg_advance = self.segments.0[seg_idx].advance;
560        let seg_clusters = self.clusters_range(glyphs_range);
561        let mut cluster_i = 0;
562
563        self.glyphs_range(glyphs_range).map(move |(font, glyphs)| {
564            let clusters = &seg_clusters[cluster_i..cluster_i + glyphs.len()];
565            cluster_i += glyphs.len();
566
567            struct Iter<'a> {
568                clusters: &'a [u32],
569                glyphs: &'a [GlyphInstance],
570            }
571            impl<'a> Iterator for Iter<'a> {
572                type Item = (u32, &'a [GlyphInstance]);
573
574                fn next(&mut self) -> Option<Self::Item> {
575                    if let Some(c) = self.clusters.first() {
576                        let end = self.clusters.iter().rposition(|rc| rc == c).unwrap();
577                        let glyphs = &self.glyphs[..=end];
578                        self.clusters = &self.clusters[end + 1..];
579                        self.glyphs = &self.glyphs[end + 1..];
580                        Some((*c, glyphs))
581                    } else {
582                        None
583                    }
584                }
585            }
586
587            let g_adv = Iter { clusters, glyphs }.map(move |(c, gs)| {
588                gi += gs.len();
589
590                let adv = if gi == glyphs_range.end() {
591                    (seg_x + seg_advance) - gs[0].point.x
592                } else {
593                    self.glyphs[gi].point.x - gs[0].point.x
594                };
595                (c, gs, adv)
596            });
597
598            (font, g_adv)
599        })
600    }
601
602    /// Bounding box size, the width is the longest line or the first or
603    /// last line width + absolute offset, the height is the bottom-most point of the last line.
604    pub fn size(&self) -> PxSize {
605        let first_width = self.first_line.origin.x.abs() + self.first_line.size.width;
606        let last_width = self.last_line.origin.x.abs() + self.last_line.size.width;
607        self.mid_size()
608            .max(PxSize::new(first_width.max(last_width), self.last_line.max_y()))
609    }
610
611    /// Size of the text, if it is not inlined.
612    pub fn block_size(&self) -> PxSize {
613        if self.lines.0.is_empty() {
614            PxSize::zero()
615        } else if self.lines.0.len() == 1 {
616            self.first_line.size
617        } else {
618            let mut s = PxSize::new(
619                self.first_line.size.width.max(self.last_line.size.width),
620                self.first_line.size.height + self.line_spacing + self.last_line.size.height,
621            );
622            if self.lines.0.len() > 2 {
623                s.width = s.width.max(self.mid_size.width);
624                s.height += self.mid_size.height + self.line_spacing;
625            }
626            s
627        }
628    }
629
630    /// Gets the first line that overflows the `max_height`. A line overflows when the line `PxRect::max_y`
631    /// is greater than `max_height`.
632    pub fn overflow_line(&self, max_height: Px) -> Option<ShapedLine<'_>> {
633        let mut y = self.first_line.max_y();
634        if y > max_height {
635            self.line(0)
636        } else if self.lines.0.len() > 1 {
637            let mid_lines = self.lines.0.len() - 2;
638            for i in 0..=mid_lines {
639                y += self.line_spacing;
640                y += self.line_height;
641                if y > max_height {
642                    return self.line(i + 1);
643                }
644            }
645
646            if self.last_line.max_y() > max_height {
647                self.line(self.lines.0.len() - 1)
648            } else {
649                None
650            }
651        } else {
652            None
653        }
654    }
655
656    fn update_mid_size(&mut self) {
657        self.mid_size = if self.lines.0.len() <= 2 {
658            PxSize::zero()
659        } else {
660            let mid_lines = &self.lines.0[1..self.lines.0.len() - 1];
661            PxSize::new(
662                Px(mid_lines.iter().map(|l| l.width).max_by(f32_cmp).unwrap_or_default().ceil() as i32),
663                Px(mid_lines.len() as i32) * self.line_height + Px((mid_lines.len() - 1) as i32) * self.line_spacing,
664            )
665        };
666    }
667
668    fn update_first_last_lines(&mut self) {
669        if self.lines.0.is_empty() {
670            self.first_line = PxRect::zero();
671            self.last_line = PxRect::zero();
672            self.align_size = PxSize::zero();
673        } else {
674            self.first_line = PxRect::from_size(PxSize::new(Px(self.lines.first_mut().width.ceil() as i32), self.line_height));
675
676            if self.lines.0.len() > 1 {
677                self.last_line.size = PxSize::new(Px(self.lines.last().width.ceil() as i32), self.line_height);
678                self.last_line.origin = PxPoint::new(Px(0), self.first_line.max_y() + self.line_spacing);
679                if self.lines.0.len() > 2 {
680                    self.last_line.origin.y += self.mid_size.height + self.line_spacing;
681                }
682            } else {
683                self.last_line = self.first_line;
684            }
685            self.align_size = self.block_size();
686        }
687    }
688
689    /// Bounding box of the mid-lines, that is the lines except the first and last.
690    pub fn mid_size(&self) -> PxSize {
691        self.mid_size
692    }
693
694    /// If the text first and last lines is defined externally by the inline layout.
695    ///
696    /// When this is `true` the shaped text only defines aligns horizontally and only the mid-lines. The vertical
697    /// offset is defined by the first line rectangle plus the [`mid_clear`].
698    ///
699    /// [`mid_clear`]: Self::mid_clear
700    pub fn is_inlined(&self) -> bool {
701        self.is_inlined
702    }
703
704    /// Last applied alignment.
705    ///
706    /// If the text is inlined only the mid-lines are aligned, and only horizontally.
707    pub fn align(&self) -> Align {
708        self.align
709    }
710
711    /// Last applied justify.
712    ///
713    /// This is the resolved mode, it is never `Auto`.
714    ///
715    /// [`align`]: Self::align
716    pub fn justify_mode(&self) -> Option<Justify> {
717        match self.justify {
718            Justify::Auto => None,
719            m => {
720                debug_assert!(self.align.is_fill_x());
721                Some(m)
722            }
723        }
724    }
725
726    /// Last applied overflow alignment.
727    ///
728    /// Only used in dimensions of the text that overflow [`align_size`].
729    ///
730    /// [`align_size`]: Self::align_size
731    pub fn overflow_align(&self) -> Align {
732        self.overflow_align
733    }
734
735    /// Last applied alignment area.
736    ///
737    /// The lines are aligned inside this size. If the text is inlined only the mid-lines are aligned and only horizontally.
738    pub fn align_size(&self) -> PxSize {
739        self.align_size
740    }
741
742    /// Last applied alignment direction.
743    ///
744    /// Note that the glyph and word directions is defined by the [`TextShapingArgs::lang`] and the computed
745    /// direction is in [`ShapedSegment::direction`].
746    pub fn direction(&self) -> LayoutDirection {
747        self.direction
748    }
749
750    /// Last applied extra spacing between the first and second lines to clear the full width of the second line in the
751    /// parent inline layout.
752    pub fn mid_clear(&self) -> Px {
753        self.mid_clear
754    }
755
756    /// Reshape text lines.
757    ///
758    /// Reshape text lines without re-wrapping, this is more efficient then fully reshaping every glyph, but may
759    /// cause overflow if called with constraints incompatible with the ones used during the full text shaping.
760    ///
761    /// The general process of shaping text is to generate a shaped-text without align during *measure*, and then reuse
762    /// this shaped text every layout that does not invalidate any property that affects the text wrap.
763    ///
764    /// Note that this method clears justify fill, of `align` is fill X you must call [`reshape_lines_justify`] after to refill.
765    ///
766    /// [`reshape_lines_justify`]: Self::reshape_lines_justify
767    pub fn reshape_lines(&mut self, args: &TextReshapingArgs) {
768        self.clear_justify_impl(args.align.is_fill_x());
769        self.reshape_line_height_and_spacing(args.line_height, args.line_spacing);
770
771        let is_inlined = args.inline_constraints.is_some();
772
773        let align_x = args.align.x(args.direction);
774        let align_y = if is_inlined { 0.fct() } else { args.align.y() };
775        let overflow_align_x = args.overflow_align.x(args.direction);
776        let overflow_align_y = if is_inlined { 0.fct() } else { args.overflow_align.y() };
777
778        let (first, mid, last, first_segs, last_segs) = if let Some(l) = &args.inline_constraints {
779            (l.first, l.mid_clear, l.last, &*l.first_segs, &*l.last_segs)
780        } else {
781            // calculate our own first & last
782            let block_size = self.block_size();
783            let align_size = args.constraints.fill_size_or(block_size);
784
785            let mut first = PxRect::from_size(self.line(0).map(|l| l.rect().size).unwrap_or_default());
786            let mut last = PxRect::from_size(
787                self.line(self.lines_len().saturating_sub(1))
788                    .map(|l| l.rect().size)
789                    .unwrap_or_default(),
790            );
791            last.origin.y = block_size.height - last.size.height;
792
793            match first.size.width.cmp(&align_size.width) {
794                cmp::Ordering::Less => first.origin.x = (align_size.width - first.size.width) * align_x,
795                cmp::Ordering::Equal => {}
796                cmp::Ordering::Greater => first.origin.x = (align_size.width - first.size.width) * overflow_align_x,
797            }
798            match last.size.width.cmp(&align_size.width) {
799                cmp::Ordering::Less => last.origin.x = (align_size.width - last.size.width) * align_x,
800                cmp::Ordering::Equal => {}
801                cmp::Ordering::Greater => last.origin.x = (align_size.width - last.size.width) * overflow_align_x,
802            }
803
804            match block_size.height.cmp(&align_size.height) {
805                cmp::Ordering::Less => {
806                    let align_y = (align_size.height - block_size.height) * align_y;
807                    first.origin.y += align_y;
808                    last.origin.y += align_y;
809                }
810                cmp::Ordering::Equal => {}
811                cmp::Ordering::Greater => {
812                    let align_y = (align_size.height - block_size.height) * overflow_align_y;
813                    first.origin.y += align_y;
814                    last.origin.y += align_y;
815                }
816            }
817
818            static EMPTY: Vec<InlineSegmentPos> = vec![];
819            (first, Px(0), last, &EMPTY, &EMPTY)
820        };
821
822        if !self.lines.0.is_empty() {
823            if self.first_line != first {
824                let first_offset = (first.origin - self.first_line.origin).cast::<f32>().cast_unit();
825
826                let first_range = self.lines.segs(0);
827                let first_glyphs = self.segments.glyphs_range(first_range);
828
829                for g in &mut self.glyphs[first_glyphs.iter()] {
830                    g.point += first_offset;
831                }
832
833                let first_line = self.lines.first_mut();
834                first_line.x_offset = first.origin.x.0 as f32;
835                // width is the same measured, unless the parent inliner changed it to fill,
836                // in that case we need the original width in `reshape_lines_justify`.
837                // first_line.width = first.size.width.0 as f32;
838            }
839            if !first_segs.is_empty() {
840                // parent set first_segs.
841                let first_range = self.lines.segs(0);
842                if first_range.len() == first_segs.len() {
843                    for i in first_range.iter() {
844                        let seg_offset = first_segs[i].x - self.segments.0[i].x;
845                        let glyphs = self.segments.glyphs(i);
846                        for g in &mut self.glyphs[glyphs.iter()] {
847                            g.point.x += seg_offset;
848                        }
849                        self.segments.0[i].x = first_segs[i].x;
850                    }
851                } else {
852                    #[cfg(debug_assertions)]
853                    {
854                        tracing::error!("expected {} segments in `first_segs`, was {}", first_range.len(), first_segs.len());
855                    }
856                }
857            }
858        }
859
860        if self.lines.0.len() > 1 {
861            if self.last_line != last {
862                // last changed and it is not first
863
864                let last_offset = (last.origin - self.last_line.origin).cast::<f32>().cast_unit();
865
866                let last_range = self.lines.segs(self.lines.0.len() - 1);
867                let last_glyphs = self.segments.glyphs_range(last_range);
868
869                for g in &mut self.glyphs[last_glyphs.iter()] {
870                    g.point += last_offset;
871                }
872
873                let last_line = self.lines.last_mut();
874                last_line.x_offset = last.origin.x.0 as f32;
875                // width is the same measured, unless the parent inliner changed it to justify, that is handled later.
876                // last_line.width = last.size.width.0 as f32;
877            }
878            if !last_segs.is_empty() {
879                // parent set last_segs.
880                let last_range = self.lines.segs(self.lines.0.len() - 1);
881
882                if last_range.len() == last_segs.len() {
883                    for i in last_range.iter() {
884                        let li = i - last_range.start();
885
886                        let seg_offset = last_segs[li].x - self.segments.0[i].x;
887                        let glyphs = self.segments.glyphs(i);
888                        for g in &mut self.glyphs[glyphs.iter()] {
889                            g.point.x += seg_offset;
890                        }
891                        self.segments.0[i].x = last_segs[li].x;
892                    }
893                } else {
894                    #[cfg(debug_assertions)]
895                    {
896                        tracing::error!("expected {} segments in `last_segs`, was {}", last_range.len(), last_segs.len());
897                    }
898                }
899            }
900        }
901
902        self.first_line = first;
903        self.last_line = last;
904
905        let block_size = self.block_size();
906        let align_size = args.constraints.fill_size_or(block_size);
907
908        if self.lines.0.len() > 2 {
909            // has mid-lines
910
911            let mid_offset = euclid::vec2::<f32, Px>(
912                0.0,
913                match block_size.height.cmp(&align_size.height) {
914                    cmp::Ordering::Less => (align_size.height - block_size.height).0 as f32 * align_y + mid.0 as f32,
915                    cmp::Ordering::Equal => mid.0 as f32,
916                    cmp::Ordering::Greater => (align_size.height - block_size.height).0 as f32 * overflow_align_y + mid.0 as f32,
917                },
918            );
919            let y_transform = mid_offset.y - self.mid_offset;
920            let align_width = align_size.width.0 as f32;
921
922            let skip_last = self.lines.0.len() - 2;
923            let mut line_start = self.lines.0[0].end;
924            for line in &mut self.lines.0[1..=skip_last] {
925                let x_offset = if line.width < align_width {
926                    (align_width - line.width) * align_x
927                } else {
928                    (align_width - line.width) * overflow_align_x
929                };
930                let x_transform = x_offset - line.x_offset;
931
932                let glyphs = self.segments.glyphs_range(IndexRange(line_start, line.end));
933                for g in &mut self.glyphs[glyphs.iter()] {
934                    g.point.x += x_transform;
935                    g.point.y += y_transform;
936                }
937                line.x_offset = x_offset;
938
939                line_start = line.end;
940            }
941
942            let y_transform_px = Px(y_transform as i32);
943            self.underline -= y_transform_px;
944            self.baseline -= y_transform_px;
945            self.overline -= y_transform_px;
946            self.strikethrough -= y_transform_px;
947            self.underline_descent -= y_transform_px;
948            self.mid_offset = mid_offset.y;
949        }
950
951        // apply baseline to the content only,
952        let baseline_offset =
953            if self.align.is_baseline() { -self.baseline } else { Px(0) } + if args.align.is_baseline() { self.baseline } else { Px(0) };
954        if baseline_offset != Px(0) {
955            let baseline_offset = baseline_offset.0 as f32;
956            for g in &mut self.glyphs {
957                g.point.y += baseline_offset;
958            }
959        }
960
961        self.align_size = align_size;
962        self.align = args.align;
963        self.direction = args.direction;
964        self.is_inlined = is_inlined;
965
966        self.debug_assert_ranges();
967    }
968    fn reshape_line_height_and_spacing(&mut self, line_height: Px, line_spacing: Px) {
969        let mut update_height = false;
970
971        if self.line_height != line_height {
972            let offset_y = (line_height - self.line_height).0 as f32;
973            let mut offset = 0.0;
974            let center = offset_y / 2.0;
975
976            self.first_line.origin.y += Px(center as i32);
977
978            for (_, r) in self.lines.iter_segs() {
979                let r = self.segments.glyphs_range(r);
980                for g in &mut self.glyphs[r.iter()] {
981                    g.point.y += offset + center;
982                }
983
984                offset += offset_y;
985            }
986
987            self.line_height = line_height;
988            update_height = true;
989        }
990
991        if self.line_spacing != line_spacing {
992            if self.lines.is_multi() {
993                let offset_y = (line_spacing - self.line_spacing).0 as f32;
994                let mut offset = offset_y;
995
996                for (_, r) in self.lines.iter_segs_skip(1) {
997                    let r = self.segments.glyphs_range(r);
998
999                    for g in &mut self.glyphs[r.iter()] {
1000                        g.point.y += offset;
1001                    }
1002
1003                    offset += offset_y;
1004                }
1005                offset -= offset_y;
1006
1007                self.last_line.origin.y += Px(offset as i32);
1008
1009                update_height = true;
1010            }
1011            self.line_spacing = line_spacing;
1012        }
1013
1014        if update_height {
1015            self.update_mid_size();
1016
1017            if !self.is_inlined {
1018                self.update_first_last_lines();
1019            }
1020        }
1021    }
1022
1023    /// Restore text to initial shape.
1024    pub fn clear_reshape(&mut self) {
1025        self.reshape_lines(&TextReshapingArgs::default());
1026    }
1027
1028    fn justify_lines_range(&self) -> ops::Range<usize> {
1029        let mut range = 0..self.lines_len();
1030
1031        if !self.is_inlined {
1032            // skip last line
1033            range.end = range.end.saturating_sub(1);
1034        }
1035        // else inlined fills the first and last line rects
1036
1037        range
1038    }
1039
1040    /// Replace the applied [`justify_mode`], if the [`align`] is fill X.
1041    ///
1042    /// [`justify_mode`]: Self::justify_mode
1043    /// [`align`]: Self::align
1044    pub fn reshape_lines_justify(&mut self, mode: Justify, lang: &Lang) {
1045        self.clear_justify_impl(true);
1046
1047        if !self.align.is_fill_x() {
1048            return;
1049        }
1050
1051        let mode = mode.resolve(lang);
1052
1053        let range = self.justify_lines_range();
1054
1055        let fill_width = self.align_size.width.0 as f32;
1056        let last_li = range.end.saturating_sub(1);
1057
1058        for li in range.clone() {
1059            let mut count;
1060            let mut space;
1061            let mut line_seg_range;
1062            let mut offset = 0.0;
1063            let mut last_is_space = false;
1064
1065            let mut fill_width = fill_width;
1066            if self.is_inlined {
1067                // inlining parent provides the fill space for the first and last segment
1068                if li == 0 {
1069                    fill_width = self.first_line.width().0 as f32;
1070                } else if li == last_li {
1071                    fill_width = self.last_line.width().0 as f32;
1072                }
1073            }
1074
1075            {
1076                // line scope
1077                let line = self.line(li).unwrap();
1078
1079                // count of space insert points
1080                count = match mode {
1081                    Justify::InterWord => line.segs().filter(|s| s.kind().is_space()).count(),
1082                    Justify::InterLetter => line
1083                        .segs()
1084                        .map(|s| {
1085                            if s.kind().is_space() {
1086                                s.clusters_count().saturating_sub(1).max(1)
1087                            } else if s.kind().is_word() {
1088                                s.clusters_count().saturating_sub(1)
1089                            } else {
1090                                0
1091                            }
1092                        })
1093                        .sum(),
1094                    Justify::Auto => unreachable!(),
1095                };
1096
1097                // space to distribute
1098                space = fill_width - self.lines.0[li].width;
1099
1100                line_seg_range = 0..line.segs_len();
1101
1102                // trim spaces at start and end
1103                let mut first_is_space = false;
1104
1105                if let Some(s) = line.seg(0)
1106                    && s.kind().is_space()
1107                    && (!self.is_inlined || li > 0 || self.first_line.origin.x == Px(0))
1108                {
1109                    // trim start, unless it inlining and the first seg is actually a continuation of another text on the same row
1110                    first_is_space = true;
1111                    count -= 1;
1112                    space += s.advance();
1113                }
1114                if let Some(s) = line.seg(line.segs_len().saturating_sub(1))
1115                    && s.kind().is_space()
1116                    && (!self.is_inlined || li < range.end - 1 || about_eq(self.first_line.size.width.0 as f32, fill_width, 1.0))
1117                {
1118                    // trim end, unless its inlining and the last seg continues
1119                    last_is_space = true;
1120                    count -= 1;
1121                    space += s.advance();
1122                }
1123                if first_is_space {
1124                    line_seg_range.start += 1;
1125                    let gsi = self.line(li).unwrap().seg_range.start();
1126                    let adv = mem::take(&mut self.segments.0[gsi].advance);
1127                    offset -= adv;
1128                    self.justified.push(adv);
1129                }
1130                if last_is_space {
1131                    line_seg_range.end = line_seg_range.end.saturating_sub(1);
1132                    let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1133                    let adv = mem::take(&mut self.segments.0[gsi].advance);
1134                    self.justified.push(adv);
1135                }
1136                if line_seg_range.start > line_seg_range.end {
1137                    line_seg_range = 0..0;
1138                }
1139            }
1140            let justify_advance = space / count as f32;
1141            self.justified.push(justify_advance);
1142
1143            for si in line_seg_range {
1144                let is_space;
1145                let glyphs_range;
1146                let gsi;
1147                {
1148                    let line = self.line(li).unwrap();
1149                    let seg = line.seg(si).unwrap();
1150
1151                    is_space = seg.kind().is_space();
1152                    glyphs_range = seg.glyphs_range();
1153                    gsi = line.seg_range.start() + si;
1154                }
1155
1156                let mut cluster = if self.clusters.is_empty() {
1157                    0
1158                } else {
1159                    self.clusters[glyphs_range.start()]
1160                };
1161                for gi in glyphs_range {
1162                    self.glyphs[gi].point.x += offset;
1163
1164                    if matches!(mode, Justify::InterLetter) && self.clusters[gi] != cluster {
1165                        cluster = self.clusters[gi];
1166                        offset += justify_advance;
1167                        self.segments.0[gsi].advance += justify_advance;
1168                    }
1169                }
1170
1171                let seg = &mut self.segments.0[gsi];
1172                seg.x += offset;
1173                if is_space {
1174                    offset += justify_advance;
1175                    seg.advance += justify_advance;
1176                }
1177            }
1178            if last_is_space {
1179                let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1180                let seg = &mut self.segments.0[gsi];
1181                debug_assert_eq!(seg.advance, 0.0);
1182                seg.x += offset;
1183            }
1184            self.justified.shrink_to_fit();
1185        }
1186
1187        self.justify = mode;
1188    }
1189
1190    /// Remove the currently applied [`justify_mode`].
1191    ///
1192    /// [`justify_mode`]: Self::justify_mode
1193    pub fn clear_justify(&mut self) {
1194        self.clear_justify_impl(false)
1195    }
1196    fn clear_justify_impl(&mut self, keep_alloc: bool) {
1197        if self.justify_mode().is_none() {
1198            return;
1199        }
1200
1201        let range = self.justify_lines_range();
1202        debug_assert!(range.len() <= self.justified.len());
1203
1204        let mut justified_alloc = mem::take(&mut self.justified);
1205
1206        let mut justified = justified_alloc.drain(..);
1207        for li in range {
1208            let mut line_seg_range;
1209            let mut last_is_space = false;
1210
1211            let mut offset = 0.0;
1212
1213            {
1214                let line = self.line(li).unwrap();
1215
1216                line_seg_range = 0..line.segs_len();
1217
1218                // trim spaces at start and end
1219                let mut first_is_space = false;
1220
1221                if let Some(s) = line.seg(0) {
1222                    first_is_space = s.kind().is_space();
1223                }
1224                if let Some(s) = line.seg(line.segs_len().saturating_sub(1)) {
1225                    last_is_space = s.kind().is_space();
1226                }
1227                if first_is_space {
1228                    line_seg_range.start += 1;
1229                    let gsi = self.line(li).unwrap().seg_range.start();
1230
1231                    let adv = justified.next().unwrap();
1232                    self.segments.0[gsi].advance = adv;
1233                    offset -= adv;
1234                }
1235                if last_is_space {
1236                    line_seg_range.end = line_seg_range.end.saturating_sub(1);
1237                    let adv = justified.next().unwrap();
1238                    let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1239                    self.segments.0[gsi].advance = adv;
1240                }
1241                if line_seg_range.start > line_seg_range.end {
1242                    line_seg_range = 0..0;
1243                }
1244            }
1245
1246            let justify_advance = justified.next().unwrap();
1247
1248            for si in line_seg_range {
1249                let is_space;
1250                let glyphs_range;
1251                let gsi;
1252                {
1253                    let line = self.line(li).unwrap();
1254                    let seg = line.seg(si).unwrap();
1255
1256                    is_space = seg.kind().is_space();
1257                    glyphs_range = seg.glyphs_range();
1258                    gsi = line.seg_range.start() + si;
1259                }
1260
1261                let mut cluster = if self.clusters.is_empty() {
1262                    0
1263                } else {
1264                    self.clusters[glyphs_range.start()]
1265                };
1266                for gi in glyphs_range {
1267                    self.glyphs[gi].point.x -= offset;
1268
1269                    if matches!(self.justify, Justify::InterLetter) && self.clusters[gi] != cluster {
1270                        cluster = self.clusters[gi];
1271                        offset += justify_advance;
1272                        self.segments.0[gsi].advance -= justify_advance;
1273                    }
1274                }
1275
1276                let seg = &mut self.segments.0[gsi];
1277                seg.x -= offset;
1278                if is_space {
1279                    offset += justify_advance;
1280                    seg.advance -= justify_advance;
1281                }
1282            }
1283            if last_is_space {
1284                let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1285                self.segments.0[gsi].x -= offset;
1286            }
1287        }
1288
1289        self.justify = Justify::Auto;
1290
1291        if keep_alloc {
1292            drop(justified);
1293            self.justified = justified_alloc;
1294        }
1295    }
1296
1297    /// Defines what lines are first and last in paragraph.
1298    pub fn paragraph_break(&self) -> ParagraphBreak {
1299        self.paragraph_break
1300    }
1301
1302    /// Height of a single line.
1303    pub fn line_height(&self) -> Px {
1304        self.line_height
1305    }
1306
1307    /// Vertical spacing in between lines of the same paragraph.
1308    pub fn line_spacing(&self) -> Px {
1309        self.line_spacing
1310    }
1311
1312    /// Vertical spacing in between paragraphs.
1313    pub fn paragraph_spacing(&self) -> Px {
1314        self.paragraph_spacing
1315    }
1316
1317    /// Vertical offset from the line bottom up that is the text baseline.
1318    ///
1319    /// The *line bottom* is the [`line_height`].
1320    ///
1321    /// [`line_height`]: Self::line_height
1322    pub fn baseline(&self) -> Px {
1323        self.baseline
1324    }
1325
1326    /// Vertical offset from the line bottom up that is the overline placement.
1327    pub fn overline(&self) -> Px {
1328        self.overline
1329    }
1330
1331    /// Vertical offset from the line bottom up that is the strikethrough placement.
1332    pub fn strikethrough(&self) -> Px {
1333        self.strikethrough
1334    }
1335
1336    /// Vertical offset from the line bottom up that is the font defined underline placement.
1337    pub fn underline(&self) -> Px {
1338        self.underline
1339    }
1340
1341    /// Vertical offset from the line bottom up that is the underline placement when the option for
1342    /// clearing all glyph descents is selected.
1343    pub fn underline_descent(&self) -> Px {
1344        self.underline_descent
1345    }
1346
1347    /// No segments.
1348    pub fn is_empty(&self) -> bool {
1349        self.segments.0.is_empty()
1350    }
1351
1352    /// Iterate over [`ShapedLine`] selections split by [`LineBreak`] or wrap.
1353    ///
1354    /// [`LineBreak`]: TextSegmentKind::LineBreak
1355    pub fn lines(&self) -> impl Iterator<Item = ShapedLine<'_>> {
1356        let just_width = self.justify_mode().map(|_| self.align_size.width);
1357        self.lines.iter_segs().enumerate().map(move |(i, (w, r))| ShapedLine {
1358            text: self,
1359            seg_range: r,
1360            index: i,
1361            width: just_width.unwrap_or_else(|| Px(w.round() as i32)),
1362        })
1363    }
1364
1365    /// Returns the number of text lines.
1366    pub fn lines_len(&self) -> usize {
1367        self.lines.0.len()
1368    }
1369
1370    /// If the first line starts in a new inline row because it could not fit in the leftover inline space.
1371    pub fn first_wrapped(&self) -> bool {
1372        self.first_wrapped
1373    }
1374
1375    /// Gets the line by index.
1376    pub fn line(&self, line_idx: usize) -> Option<ShapedLine<'_>> {
1377        if line_idx >= self.lines.0.len() {
1378            None
1379        } else {
1380            self.lines.iter_segs_skip(line_idx).next().map(move |(w, r)| ShapedLine {
1381                text: self,
1382                seg_range: r,
1383                index: line_idx,
1384                width: Px(w.round() as i32),
1385            })
1386        }
1387    }
1388
1389    /// Create an empty [`ShapedText`] with the same metrics as `self`.
1390    pub fn empty(&self) -> ShapedText {
1391        ShapedText {
1392            glyphs: vec![],
1393            clusters: vec![],
1394            segments: GlyphSegmentVec(vec![]),
1395            lines: LineRangeVec(vec![LineRange {
1396                end: 0,
1397                width: 0.0,
1398                x_offset: 0.0,
1399                directions: LayoutDirections::empty(),
1400            }]),
1401            fonts: FontRangeVec(vec![FontRange {
1402                font: self.fonts.font(0).clone(),
1403                end: 0,
1404            }]),
1405            images: vec![],
1406            orig_line_height: self.orig_line_height,
1407            orig_line_spacing: self.orig_line_spacing,
1408            orig_first_line: PxSize::zero(),
1409            orig_last_line: PxSize::zero(),
1410            line_height: self.orig_line_height,
1411            line_spacing: self.orig_line_spacing,
1412            paragraph_spacing: self.paragraph_spacing,
1413            paragraph_break: self.paragraph_break,
1414            baseline: self.baseline,
1415            overline: self.overline,
1416            strikethrough: self.strikethrough,
1417            underline: self.underline,
1418            underline_descent: self.underline_descent,
1419            mid_offset: 0.0,
1420            align_size: PxSize::zero(),
1421            align: Align::TOP_LEFT,
1422            justify: Justify::Auto,
1423            justified: vec![],
1424            overflow_align: Align::TOP_LEFT,
1425            direction: LayoutDirection::LTR,
1426            first_wrapped: false,
1427            first_line: PxRect::zero(),
1428            mid_clear: Px(0),
1429            is_inlined: false,
1430            mid_size: PxSize::zero(),
1431            last_line: PxRect::zero(),
1432            has_colored_glyphs: false,
1433        }
1434    }
1435
1436    /// Check if any line can be better wrapped given the new wrap config.
1437    ///
1438    /// Note that a new [`ShapedText`] must be generated to *rewrap*.
1439    pub fn can_rewrap(&self, max_width: Px) -> bool {
1440        for line in self.lines() {
1441            if line.width > max_width || line.started_by_wrap() {
1442                return true;
1443            }
1444        }
1445        false
1446    }
1447
1448    fn debug_assert_ranges(&self) {
1449        #[cfg(debug_assertions)]
1450        {
1451            #[allow(unused)]
1452            macro_rules! trace_assert {
1453                ($cond:expr $(,)?) => {
1454                    #[allow(clippy::all)]
1455                    if !($cond) {
1456                        tracing::error!("{}", stringify!($cond));
1457                        return;
1458                    }
1459                };
1460                ($cond:expr, $($arg:tt)+) => {
1461                    #[allow(clippy::all)]
1462                    if !($cond) {
1463                        tracing::error!($($arg)*);
1464                        return;
1465                    }
1466                };
1467            }
1468
1469            let mut prev_seg_end = 0;
1470            for seg in &self.segments.0 {
1471                trace_assert!(seg.end >= prev_seg_end);
1472                prev_seg_end = seg.end;
1473            }
1474            trace_assert!(self.segments.0.last().map(|s| s.end == self.glyphs.len()).unwrap_or(true));
1475
1476            let mut prev_line_end = 0;
1477            for (i, line) in self.lines.0.iter().enumerate() {
1478                trace_assert!(line.end >= prev_line_end);
1479                trace_assert!(line.width >= 0.0);
1480
1481                let line_max = line.x_offset + line.width;
1482                let glyphs = self.segments.glyphs_range(IndexRange(prev_line_end, line.end));
1483                for g in &self.glyphs[glyphs.iter()] {
1484                    // false positive in cases of heavy use of combining chars
1485                    // only observed in "Zalgo" text, remove if we there is a legitimate
1486                    // Script that causing this error.
1487                    trace_assert!(
1488                        g.point.x <= line_max,
1489                        "glyph.x({:?}) > line[{i}].x+width({:?})",
1490                        g.point.x,
1491                        line_max
1492                    );
1493                }
1494
1495                let seg_width = self.segments.0[prev_line_end..line.end].iter().map(|s| s.advance).sum::<f32>();
1496                trace_assert!(
1497                    seg_width <= line.width,
1498                    "seg_width({:?}) > line[{i}].width({:?})",
1499                    seg_width,
1500                    line.width,
1501                );
1502
1503                prev_line_end = line.end;
1504            }
1505            trace_assert!(self.lines.0.last().map(|l| l.end == self.segments.0.len()).unwrap_or(true));
1506
1507            let mut prev_font_end = 0;
1508            for font in &self.fonts.0 {
1509                trace_assert!(font.end >= prev_font_end);
1510                prev_font_end = font.end;
1511            }
1512            trace_assert!(self.fonts.0.last().map(|f| f.end == self.glyphs.len()).unwrap_or(true));
1513        }
1514    }
1515
1516    /// Gets the top-middle origin for a caret visual that marks the insert `index` in the string.
1517    pub fn caret_origin(&self, caret: CaretIndex, full_text: &str) -> PxPoint {
1518        let index = caret.index;
1519        let mut end_line = None;
1520        for line in self.line(caret.line).into_iter().chain(self.lines()) {
1521            for seg in line.segs() {
1522                let txt_range = seg.text_range();
1523                if !txt_range.contains(&index) {
1524                    continue;
1525                }
1526                let local_index = index - txt_range.start;
1527                let is_rtl = seg.direction().is_rtl();
1528
1529                let seg_rect = seg.rect();
1530                let mut origin = seg_rect.origin;
1531
1532                let clusters = seg.clusters();
1533                let mut cluster_i = 0;
1534                let mut search_lig = true;
1535
1536                if is_rtl {
1537                    for (i, c) in clusters.iter().enumerate().rev() {
1538                        match (*c as usize).cmp(&local_index) {
1539                            cmp::Ordering::Less => {
1540                                cluster_i = i;
1541                            }
1542                            cmp::Ordering::Equal => {
1543                                cluster_i = i;
1544                                search_lig = false;
1545                                break;
1546                            }
1547                            cmp::Ordering::Greater => break,
1548                        }
1549                    }
1550                } else {
1551                    for (i, c) in clusters.iter().enumerate() {
1552                        match (*c as usize).cmp(&local_index) {
1553                            cmp::Ordering::Less => {
1554                                cluster_i = i;
1555                            }
1556                            cmp::Ordering::Equal => {
1557                                cluster_i = i;
1558                                search_lig = false;
1559                                break;
1560                            }
1561                            cmp::Ordering::Greater => break,
1562                        }
1563                    }
1564                }
1565
1566                let mut origin_x = origin.x.0 as f32;
1567
1568                // glyphs are always in display order (LTR) and map
1569                // to each cluster entry.
1570                //
1571                // in both LTR and RTL we sum advance until `cluster_i` is found,
1572                // but in RTL we sum *back* to the char (so it needs to be covered +1)
1573                let mut glyph_take = cluster_i;
1574                if is_rtl {
1575                    glyph_take += 1;
1576                }
1577
1578                let mut search_lig_data = None;
1579
1580                'outer: for (font, glyphs) in seg.glyphs_with_x_advance() {
1581                    for (g, advance) in glyphs {
1582                        search_lig_data = Some((font, g.index, advance));
1583
1584                        if glyph_take == 0 {
1585                            break 'outer;
1586                        }
1587                        origin_x += advance;
1588                        glyph_take -= 1;
1589                    }
1590                }
1591
1592                if search_lig && let Some((font, g_index, advance)) = search_lig_data {
1593                    let lig_start = txt_range.start + clusters[cluster_i] as usize;
1594                    let lig_end = if is_rtl {
1595                        if cluster_i == 0 {
1596                            txt_range.end
1597                        } else {
1598                            txt_range.start + clusters[cluster_i - 1] as usize
1599                        }
1600                    } else {
1601                        clusters
1602                            .get(cluster_i + 1)
1603                            .map(|c| txt_range.start + *c as usize)
1604                            .unwrap_or_else(|| txt_range.end)
1605                    };
1606
1607                    let maybe_lig = &full_text[lig_start..lig_end];
1608
1609                    let lig_len = unicode_segmentation::UnicodeSegmentation::grapheme_indices(maybe_lig, true).count();
1610                    if lig_len > 1 {
1611                        // is ligature
1612
1613                        let lig_taken = &full_text[lig_start..index];
1614                        let lig_taken = unicode_segmentation::UnicodeSegmentation::grapheme_indices(lig_taken, true).count();
1615
1616                        for (i, lig_advance) in font.ligature_caret_offsets(g_index).enumerate() {
1617                            if i == lig_taken {
1618                                // font provided ligature caret for index
1619                                origin_x += lig_advance;
1620                                search_lig = false;
1621                                break;
1622                            }
1623                        }
1624
1625                        if search_lig {
1626                            // synthetic lig. caret
1627                            let lig_advance = advance * (lig_taken as f32 / lig_len as f32);
1628
1629                            if is_rtl {
1630                                origin_x -= lig_advance;
1631                            } else {
1632                                origin_x += lig_advance;
1633                            }
1634                        }
1635                    }
1636                }
1637
1638                origin.x = Px(origin_x.round() as _);
1639                return origin;
1640            }
1641
1642            if line.index == caret.line && line.text_range().end == index && line.ended_by_wrap() {
1643                // is at the end of a wrap.
1644                end_line = Some(line.index);
1645                break;
1646            }
1647        }
1648
1649        // position at the end of the end_line.
1650        let line_end = end_line.unwrap_or_else(|| self.lines_len().saturating_sub(1));
1651        if let Some(line) = self.line(line_end) {
1652            let rect = line.rect();
1653            if self.direction().is_rtl() {
1654                // top-left of last line if it the text is RTL overall.
1655                PxPoint::new(rect.min_x(), rect.min_y())
1656            } else {
1657                // top-right of last line for LTR
1658                PxPoint::new(rect.max_x(), rect.min_y())
1659            }
1660        } else {
1661            PxPoint::zero()
1662        }
1663    }
1664
1665    /// Gets the line that contains the `y` offset or is nearest to it.
1666    pub fn nearest_line(&self, y: Px) -> Option<ShapedLine<'_>> {
1667        let first_line_max_y = self.first_line.max_y();
1668        if first_line_max_y >= y {
1669            self.line(0)
1670        } else if self.last_line.min_y() <= y {
1671            self.line(self.lines_len().saturating_sub(1))
1672        } else {
1673            let y = y - first_line_max_y;
1674            let line = (y / self.line_height()).0 as usize + 1;
1675            self.lines.iter_segs_skip(line).next().map(move |(w, r)| ShapedLine {
1676                text: self,
1677                seg_range: r,
1678                index: line,
1679                width: Px(w.round() as i32),
1680            })
1681        }
1682    }
1683
1684    /// Changes the caret line if the current line cannot contain the current char byte index.
1685    ///
1686    /// This retains the same line at ambiguous points at the end/start of wrapped lines.
1687    pub fn snap_caret_line(&self, mut caret: CaretIndex) -> CaretIndex {
1688        for line in self.lines() {
1689            let range = line.text_range();
1690
1691            if range.start == caret.index {
1692                // at start that can be by wrap
1693                if line.started_by_wrap() {
1694                    if caret.line >= line.index {
1695                        caret.line = line.index;
1696                    } else {
1697                        caret.line = line.index.saturating_sub(1);
1698                    }
1699                } else {
1700                    caret.line = line.index;
1701                }
1702                return caret;
1703            } else if range.contains(&caret.index) {
1704                // inside of line
1705                caret.line = line.index;
1706                return caret;
1707            }
1708        }
1709        caret.line = self.lines.0.len().saturating_sub(1);
1710        caret
1711    }
1712
1713    /// Gets a full overflow analysis.
1714    pub fn overflow_info(&self, max_size: PxSize, overflow_suffix_width: Px) -> Option<TextOverflowInfo> {
1715        // check y overflow
1716
1717        let (last_line, overflow_line) = match self.overflow_line(max_size.height) {
1718            Some(l) => {
1719                if l.index == 0 {
1720                    // all text overflows
1721                    return Some(TextOverflowInfo {
1722                        line: 0,
1723                        text_char: 0,
1724                        included_glyphs: smallvec::smallvec![],
1725                        suffix_origin: l.rect().origin.cast().cast_unit(),
1726                    });
1727                } else {
1728                    (self.line(l.index - 1).unwrap(), l.index)
1729                }
1730            }
1731            None => (self.line(self.lines_len().saturating_sub(1))?, self.lines_len()),
1732        };
1733
1734        // check x overflow
1735
1736        let max_width = max_size.width - overflow_suffix_width;
1737
1738        if last_line.width <= max_width {
1739            // No x overflow
1740            return if overflow_line < self.lines_len() {
1741                Some(TextOverflowInfo {
1742                    line: overflow_line,
1743                    text_char: last_line.text_range().end,
1744                    included_glyphs: smallvec::smallvec_inline![0..last_line.glyphs_range().end()],
1745                    suffix_origin: {
1746                        let r = last_line.rect();
1747                        let mut o = r.origin;
1748                        match self.direction {
1749                            LayoutDirection::LTR => o.x += r.width(),
1750                            LayoutDirection::RTL => o.x -= overflow_suffix_width,
1751                        }
1752                        o.cast().cast_unit()
1753                    },
1754                })
1755            } else {
1756                None
1757            };
1758        }
1759
1760        let directions = last_line.directions();
1761        if directions == LayoutDirections::BIDI {
1762            let mut included_glyphs = smallvec::SmallVec::<[ops::Range<usize>; 1]>::new_const();
1763
1764            let min_x = match self.direction {
1765                LayoutDirection::LTR => Px(0),
1766                LayoutDirection::RTL => last_line.rect().max_x() - max_width,
1767            };
1768            let max_x = min_x + max_width;
1769
1770            let mut end_seg = None;
1771
1772            for seg in last_line.segs() {
1773                let (x, width) = seg.x_width();
1774                let seg_max_x = x + width;
1775
1776                if x < max_x && seg_max_x >= min_x {
1777                    let mut glyphs_range = seg.glyphs_range().iter();
1778                    let mut text_range = seg.text_range();
1779                    if x < min_x {
1780                        if let Some((c, g)) = seg.overflow_char_glyph((width - (min_x - x)).0 as f32) {
1781                            glyphs_range.start += g + 1;
1782                            text_range.start += c;
1783                        }
1784                    } else if seg_max_x > max_x
1785                        && let Some((c, g)) = seg.overflow_char_glyph((width - seg_max_x - max_x).0 as f32)
1786                    {
1787                        glyphs_range.end -= g;
1788                        text_range.end -= c;
1789                    }
1790
1791                    if let Some(l) = included_glyphs.last_mut() {
1792                        if l.end == glyphs_range.start {
1793                            l.end = glyphs_range.end;
1794                        } else if glyphs_range.end == l.start {
1795                            l.start = glyphs_range.start;
1796                        } else {
1797                            included_glyphs.push(glyphs_range.clone());
1798                        }
1799                    } else {
1800                        included_glyphs.push(glyphs_range.clone());
1801                    }
1802
1803                    match self.direction {
1804                        LayoutDirection::LTR => {
1805                            if let Some((sx, se, gr, tr)) = &mut end_seg {
1806                                if x < *sx {
1807                                    *sx = x;
1808                                    *se = seg;
1809                                    *gr = glyphs_range;
1810                                    *tr = text_range;
1811                                }
1812                            } else {
1813                                end_seg = Some((x, seg, glyphs_range, text_range));
1814                            }
1815                        }
1816                        LayoutDirection::RTL => {
1817                            if let Some((smx, se, gr, tr)) = &mut end_seg {
1818                                if seg_max_x < *smx {
1819                                    *smx = seg_max_x;
1820                                    *se = seg;
1821                                    *gr = glyphs_range;
1822                                    *tr = text_range;
1823                                }
1824                            } else {
1825                                end_seg = Some((seg_max_x, seg, glyphs_range, text_range));
1826                            }
1827                        }
1828                    }
1829                }
1830            }
1831
1832            if let Some((_, seg, glyphs_range, text_range)) = end_seg {
1833                Some(match self.direction {
1834                    LayoutDirection::LTR => TextOverflowInfo {
1835                        line: overflow_line,
1836                        text_char: text_range.end,
1837                        included_glyphs,
1838                        suffix_origin: {
1839                            let r = seg.rect();
1840                            let seg_range = seg.glyphs_range().iter();
1841                            let mut o = r.origin.cast().cast_unit();
1842                            let mut w = r.width();
1843                            if seg_range != glyphs_range
1844                                && let Some(g) = seg.glyph(glyphs_range.end - seg_range.start)
1845                            {
1846                                o.x = g.1.point.x;
1847                                w = Px(0);
1848                            }
1849                            o.x += w.0 as f32;
1850                            o
1851                        },
1852                    },
1853                    LayoutDirection::RTL => TextOverflowInfo {
1854                        line: overflow_line,
1855                        text_char: text_range.start,
1856                        included_glyphs,
1857                        suffix_origin: {
1858                            let r = seg.rect();
1859                            let mut o = r.origin.cast().cast_unit();
1860                            let seg_range = seg.glyphs_range().iter();
1861                            if seg_range != glyphs_range
1862                                && let Some(g) = seg.glyph(glyphs_range.start - seg_range.start)
1863                            {
1864                                o.x = g.1.point.x;
1865                            }
1866                            o.x -= overflow_suffix_width.0 as f32;
1867                            o
1868                        },
1869                    },
1870                })
1871            } else {
1872                None
1873            }
1874        } else {
1875            // single direction overflow
1876            let mut max_width_f32 = max_width.0 as f32;
1877            for seg in last_line.segs() {
1878                let seg_advance = seg.advance();
1879                max_width_f32 -= seg_advance;
1880                if max_width_f32 <= 0.0 {
1881                    let seg_text_range = seg.text_range();
1882                    let seg_glyphs_range = seg.glyphs_range();
1883
1884                    if directions == LayoutDirections::RTL {
1885                        let (c, g) = match seg.overflow_char_glyph(seg_advance + max_width_f32) {
1886                            Some(r) => r,
1887                            None => (seg_text_range.len(), seg_glyphs_range.len()),
1888                        };
1889
1890                        return Some(TextOverflowInfo {
1891                            line: overflow_line,
1892                            text_char: seg_text_range.start + c,
1893                            included_glyphs: smallvec::smallvec![
1894                                0..seg_glyphs_range.start(),
1895                                seg_glyphs_range.start() + g + 1..seg_glyphs_range.end()
1896                            ],
1897                            suffix_origin: {
1898                                let mut o = if let Some(g) = seg.glyph(g + 1) {
1899                                    euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1900                                } else {
1901                                    let rect = seg.rect();
1902                                    let mut o = rect.origin.cast().cast_unit();
1903                                    o.x += seg.advance();
1904                                    o
1905                                };
1906                                o.x -= overflow_suffix_width.0 as f32;
1907                                o
1908                            },
1909                        });
1910                    } else {
1911                        // LTR or empty
1912
1913                        let (c, g) = match seg.overflow_char_glyph((max_width - seg.x_width().0).0 as f32) {
1914                            Some(r) => r,
1915                            None => (seg_text_range.len(), seg_glyphs_range.len()),
1916                        };
1917
1918                        return Some(TextOverflowInfo {
1919                            line: overflow_line,
1920                            text_char: seg_text_range.start + c,
1921                            included_glyphs: smallvec::smallvec_inline![0..seg_glyphs_range.start() + g],
1922                            suffix_origin: {
1923                                if let Some(g) = seg.glyph(g) {
1924                                    euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1925                                } else {
1926                                    let rect = seg.rect();
1927                                    let mut o = rect.origin.cast().cast_unit();
1928                                    o.x += seg.advance();
1929                                    o
1930                                }
1931                            },
1932                        });
1933                    }
1934                }
1935            }
1936            // no overflow, rounding issue?
1937            None
1938        }
1939    }
1940
1941    /// Rectangles of the text selected by `range`.
1942    ///
1943    /// The [`CaretIndex::line`] must be valid in the `range` value.
1944    pub fn highlight_rects(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = PxRect> + '_ {
1945        let start_origin = self.caret_origin(range.start, full_txt).x;
1946        let end_origin = self.caret_origin(range.end, full_txt).x;
1947
1948        MergingRectIter::new(
1949            self.lines()
1950                .skip(range.start.line)
1951                .take(range.end.line + 1 - range.start.line)
1952                .flat_map(|l| l.segs())
1953                .skip_while(move |s| s.text_end() <= range.start.index)
1954                .take_while(move |s| s.text_start() < range.end.index)
1955                .map(move |s| {
1956                    let mut r = s.rect();
1957
1958                    if s.text_start() <= range.start.index {
1959                        // first segment in selection
1960
1961                        match s.direction() {
1962                            LayoutDirection::LTR => {
1963                                r.size.width = r.max_x() - start_origin;
1964                                r.origin.x = start_origin;
1965                            }
1966                            LayoutDirection::RTL => {
1967                                r.size.width = start_origin - r.origin.x;
1968                            }
1969                        }
1970                    }
1971                    if s.text_end() > range.end.index {
1972                        // last segment in selection
1973
1974                        match s.direction() {
1975                            LayoutDirection::LTR => {
1976                                r.size.width = end_origin - r.origin.x;
1977                            }
1978                            LayoutDirection::RTL => {
1979                                r.size.width = r.max_x() - end_origin;
1980                                r.origin.x = end_origin;
1981                            }
1982                        }
1983                    }
1984
1985                    r
1986                }),
1987        )
1988    }
1989
1990    /// Underlines of the text selected by `range`.
1991    ///
1992    /// Yields each line start point and width. The underline does not skip.
1993    ///
1994    /// The [`CaretIndex::line`] must be valid in the `range` value.
1995    pub fn highlight_underlines(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = (PxPoint, Px)> + '_ {
1996        let offset = self.underline();
1997        self.highlight_rects(range, full_txt).map(move |r| {
1998            let mut origin = r.origin;
1999            origin.y = r.max_y() - offset;
2000            (origin, r.width())
2001        })
2002    }
2003
2004    /// Clip under/overline to a text `clip_range` area, if `clip_out` only lines outside the range are visible.
2005    pub fn clip_lines(
2006        &self,
2007        clip_range: ops::Range<CaretIndex>,
2008        clip_out: bool,
2009        txt: &str,
2010        lines: impl Iterator<Item = (PxPoint, Px)>,
2011    ) -> Vec<(PxPoint, Px)> {
2012        let clips: Vec<_> = self.highlight_rects(clip_range, txt).collect();
2013
2014        let mut out_lines = vec![];
2015
2016        if clip_out {
2017            let mut exclude_buf = vec![];
2018            for (origin, width) in lines {
2019                let line_max = origin.x + width;
2020
2021                for clip in clips.iter() {
2022                    if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2023                        // line contains
2024                        if origin.x < clip.max_x() && line_max > clip.origin.x {
2025                            // intersects
2026                            exclude_buf.push((clip.origin.x, clip.max_x()));
2027                        }
2028                    }
2029                }
2030
2031                if !exclude_buf.is_empty() {
2032                    // clips don't overlap, enforce LTR
2033                    exclude_buf.sort_by_key(|(s, _)| *s);
2034
2035                    if origin.x < exclude_buf[0].0 {
2036                        // bit before the first clip
2037                        out_lines.push((origin, exclude_buf[0].0 - origin.x));
2038                    }
2039                    let mut blank_start = exclude_buf[0].1;
2040                    for (clip_start, clip_end) in exclude_buf.drain(..).skip(1) {
2041                        if clip_start > blank_start {
2042                            // space between clips
2043                            if line_max > clip_start {
2044                                // bit in-between two clips
2045                                out_lines.push((PxPoint::new(blank_start, origin.y), line_max.min(clip_start) - blank_start));
2046                            }
2047                            blank_start = clip_end;
2048                        }
2049                    }
2050                    if line_max > blank_start {
2051                        // bit after the last clip
2052                        out_lines.push((PxPoint::new(blank_start, origin.y), line_max - blank_start));
2053                    }
2054                } else {
2055                    // not clipped
2056                    out_lines.push((origin, width));
2057                }
2058            }
2059        } else {
2060            let mut include_buf = vec![];
2061            for (origin, width) in lines {
2062                let line_max = origin.x + width;
2063
2064                for clip in clips.iter() {
2065                    if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2066                        // line contains
2067                        if origin.x < clip.max_x() && line_max > clip.origin.x {
2068                            // intersects
2069                            include_buf.push((clip.origin.x, clip.max_x()));
2070                        }
2071                    }
2072                }
2073
2074                if !include_buf.is_empty() {
2075                    include_buf.sort_by_key(|(s, _)| *s);
2076
2077                    for (clip_start, clip_end) in include_buf.drain(..) {
2078                        let start = clip_start.max(origin.x);
2079                        let end = clip_end.min(line_max);
2080
2081                        out_lines.push((PxPoint::new(start, origin.y), end - start));
2082                    }
2083
2084                    include_buf.clear();
2085                }
2086            }
2087        }
2088
2089        out_lines
2090    }
2091}
2092
2093struct ImageGlyphsIter<'a, G>
2094where
2095    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2096{
2097    glyphs: G,
2098    glyphs_i: u32,
2099    images: &'a [(u32, GlyphImage)],
2100    maybe_img: Option<(&'a Font, &'a [GlyphInstance])>,
2101}
2102impl<'a, G> Iterator for ImageGlyphsIter<'a, G>
2103where
2104    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2105{
2106    type Item = (&'a Font, ShapedImageGlyphs<'a>);
2107
2108    fn next(&mut self) -> Option<Self::Item> {
2109        loop {
2110            if let Some((font, glyphs)) = &mut self.maybe_img {
2111                // new glyph sequence or single emoji(maybe img)
2112
2113                // advance images to the next in or after glyph sequence
2114                while self.images.first().map(|(i, _)| *i < self.glyphs_i).unwrap_or(false) {
2115                    self.images = &self.images[1..];
2116                }
2117
2118                if let Some((i, img)) = self.images.first() {
2119                    // if there is still images
2120                    if *i == self.glyphs_i {
2121                        // if the next glyph is replaced by image
2122                        self.glyphs_i += 1;
2123                        let mut size = img.0.with(|i| i.size()).cast::<f32>();
2124                        let scale = font.size().0 as f32 / size.width.max(size.height);
2125                        size *= scale;
2126                        let r = (
2127                            *font,
2128                            ShapedImageGlyphs::Image {
2129                                rect: euclid::Rect::new(glyphs[0].point - euclid::vec2(0.0, size.height), size),
2130                                base_glyph: glyphs[0].index,
2131                                img: &img.0,
2132                            },
2133                        );
2134                        *glyphs = &glyphs[1..];
2135                        if glyphs.is_empty() {
2136                            self.maybe_img = None;
2137                        }
2138                        return Some(r);
2139                    } else {
2140                        // if the next glyph is not replaced by image, yield slice to end or next image
2141                        let normal = &glyphs[..glyphs.len().min(*i as _)];
2142                        self.glyphs_i += normal.len() as u32;
2143
2144                        *glyphs = &glyphs[normal.len()..];
2145                        let r = (*font, ShapedImageGlyphs::Normal(normal));
2146
2147                        if glyphs.is_empty() {
2148                            self.maybe_img = None;
2149                        }
2150                        return Some(r);
2151                    }
2152                } else {
2153                    // if there are no more images
2154                    let r = (*font, ShapedImageGlyphs::Normal(glyphs));
2155                    self.maybe_img = None;
2156                    return Some(r);
2157                }
2158            } else if let Some(seq) = self.glyphs.next() {
2159                // all sequences can contain images
2160                self.maybe_img = Some(seq);
2161            } else {
2162                // no more glyphs to yield
2163                return None;
2164            }
2165        }
2166    }
2167}
2168
2169struct ColoredGlyphsIter<'a, G>
2170where
2171    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2172{
2173    glyphs: G,
2174    maybe_colored: Option<(&'a Font, &'a [GlyphInstance])>,
2175}
2176impl<'a, G> Iterator for ColoredGlyphsIter<'a, G>
2177where
2178    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2179{
2180    type Item = (&'a Font, ShapedColoredGlyphs<'a>);
2181
2182    fn next(&mut self) -> Option<Self::Item> {
2183        loop {
2184            if let Some((font, glyphs)) = self.maybe_colored {
2185                // maybe-colored iter
2186
2187                let color_glyphs = font.face().color_glyphs();
2188
2189                for (i, g) in glyphs.iter().enumerate() {
2190                    if let Some(c_glyphs) = color_glyphs.glyph(g.index) {
2191                        // colored yield
2192
2193                        let next_start = i + 1;
2194                        if next_start < glyphs.len() {
2195                            // continue maybe-colored iter
2196                            self.maybe_colored = Some((font, &glyphs[next_start..]));
2197                        } else {
2198                            // continue normal iter
2199                            self.maybe_colored = None;
2200                        }
2201
2202                        return Some((
2203                            font,
2204                            ShapedColoredGlyphs::Colored {
2205                                point: g.point,
2206                                base_glyph: g.index,
2207                                glyphs: c_glyphs,
2208                            },
2209                        ));
2210                    }
2211                }
2212                // enter normal iter
2213                self.maybe_colored = None;
2214
2215                // last normal in maybe-colored yield
2216                debug_assert!(!glyphs.is_empty());
2217                return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2218            } else if let Some((font, glyphs)) = self.glyphs.next() {
2219                // normal iter
2220
2221                let color_glyphs = font.face().color_glyphs();
2222                if color_glyphs.is_empty() {
2223                    return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2224                } else {
2225                    // enter maybe-colored iter
2226                    self.maybe_colored = Some((font, glyphs));
2227                    continue;
2228                }
2229            } else {
2230                return None;
2231            }
2232        }
2233    }
2234}
2235
2236/// Info about a shaped text overflow in constraint.
2237///
2238/// Can be computed using [`ShapedText::overflow_info`].
2239#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2240#[non_exhaustive]
2241pub struct TextOverflowInfo {
2242    /// First overflow line.
2243    ///
2244    /// All segments in this line and next lines are fully overflown. The previous line may
2245    /// be partially overflown, the lines before that are fully visible.
2246    ///
2247    /// Is the [`ShapedText::lines_len`] if the last line is fully visible.
2248    pub line: usize,
2249
2250    /// First overflow character in the text.
2251    ///
2252    /// Note that if overflow is not wrapping (single text line) the char may not cover all visible
2253    /// glyphs in the line if it is bidirectional.
2254    pub text_char: usize,
2255
2256    /// Glyphs not overflown in the last not overflown line.
2257    ///
2258    /// If the line is not bidirectional this will be a single range covering the not overflow glyphs,
2259    /// if it is bidi multiple ranges are possible due to bidi reordering.
2260    pub included_glyphs: smallvec::SmallVec<[ops::Range<usize>; 1]>,
2261
2262    /// Placement of the suffix (ellipses or custom).
2263    ///
2264    /// The suffix must be of the width given to [`ShapedText::overflow_info`] and the same line height
2265    /// as the text.
2266    pub suffix_origin: euclid::Point2D<f32, Px>,
2267}
2268
2269trait FontListRef {
2270    /// Shape segment, try fallback fonts if a glyph in the segment is not resolved.
2271    fn shape_segment<R>(
2272        &self,
2273        seg: &str,
2274        word_ctx_key: &WordContextKey,
2275        features: &[rustybuzz::Feature],
2276        out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2277    ) -> R;
2278}
2279impl FontListRef for [Font] {
2280    fn shape_segment<R>(
2281        &self,
2282        seg: &str,
2283        word_ctx_key: &WordContextKey,
2284        features: &[rustybuzz::Feature],
2285        out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2286    ) -> R {
2287        let mut out = Some(out);
2288        let last = self.len() - 1;
2289        for font in &self[..last] {
2290            let r = font.shape_segment(seg, word_ctx_key, features, |seg| {
2291                if seg.glyphs.iter().all(|g| g.index != 0) {
2292                    Some(out.take().unwrap()(seg, font))
2293                } else {
2294                    None
2295                }
2296            });
2297            if let Some(r) = r {
2298                return r;
2299            }
2300        }
2301        self[last].shape_segment(seg, word_ctx_key, features, move |seg| out.unwrap()(seg, &self[last]))
2302    }
2303}
2304
2305struct ShapedTextBuilder {
2306    out: ShapedText,
2307
2308    line_height: f32,
2309    line_spacing: f32,
2310    paragraph_spacing: f32,
2311    word_spacing: f32,
2312    letter_spacing: f32,
2313    paragraph_indent: (f32, bool),
2314    max_width: f32,
2315    break_words: bool,
2316    hyphen_glyphs: (ShapedSegmentData, Font),
2317    tab_x_advance: f32,
2318    tab_index: u32,
2319    hyphens: Hyphens,
2320    lang: Lang,
2321
2322    origin: euclid::Point2D<f32, ()>,
2323    allow_first_wrap: bool,
2324    first_line_max: f32,
2325    mid_clear_min: f32,
2326    max_line_x: f32,
2327    text_seg_end: usize,
2328    line_has_ltr: bool,
2329    line_has_rtl: bool,
2330}
2331impl ShapedTextBuilder {
2332    fn actual_max_width(&self) -> f32 {
2333        if self.out.lines.0.is_empty() && !self.out.first_wrapped {
2334            self.first_line_max.min(self.max_width)
2335        } else {
2336            self.max_width
2337        }
2338    }
2339
2340    fn shape_text(fonts: &[Font], text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
2341        let _span = tracing::trace_span!("shape_text").entered();
2342        let mut t = Self {
2343            out: ShapedText {
2344                glyphs: Default::default(),
2345                clusters: Default::default(),
2346                segments: Default::default(),
2347                lines: Default::default(),
2348                fonts: Default::default(),
2349                line_height: Default::default(),
2350                line_spacing: Default::default(),
2351                orig_line_height: Default::default(),
2352                orig_line_spacing: Default::default(),
2353                orig_first_line: Default::default(),
2354                orig_last_line: Default::default(),
2355                paragraph_break: Default::default(),
2356                paragraph_spacing: Default::default(),
2357                baseline: Default::default(),
2358                overline: Default::default(),
2359                strikethrough: Default::default(),
2360                underline: Default::default(),
2361                underline_descent: Default::default(),
2362                mid_offset: 0.0,
2363                align_size: PxSize::zero(),
2364                align: Align::TOP_LEFT,
2365                justify: Justify::Auto,
2366                justified: vec![],
2367                overflow_align: Align::TOP_LEFT,
2368                direction: LayoutDirection::LTR,
2369                first_wrapped: false,
2370                is_inlined: config.inline_constraints.is_some(),
2371                first_line: PxRect::zero(),
2372                mid_clear: Px(0),
2373                mid_size: PxSize::zero(),
2374                last_line: PxRect::zero(),
2375                has_colored_glyphs: false,
2376                images: vec![],
2377            },
2378
2379            line_height: 0.0,
2380            line_spacing: 0.0,
2381            paragraph_spacing: 0.0,
2382            word_spacing: 0.0,
2383            letter_spacing: 0.0,
2384            paragraph_indent: (0.0, false),
2385            max_width: 0.0,
2386            break_words: false,
2387            hyphen_glyphs: (ShapedSegmentData::default(), fonts[0].clone()),
2388            tab_x_advance: 0.0,
2389            tab_index: 0,
2390            hyphens: config.hyphens,
2391            lang: config.lang.clone(),
2392            allow_first_wrap: false,
2393
2394            origin: euclid::point2(0.0, 0.0),
2395            first_line_max: f32::INFINITY,
2396            mid_clear_min: 0.0,
2397            max_line_x: 0.0,
2398            text_seg_end: 0,
2399            line_has_ltr: false,
2400            line_has_rtl: false,
2401        };
2402
2403        let mut word_ctx_key = WordContextKey::new(&config.lang, config.direction, &config.font_features);
2404
2405        let metrics = fonts[0].metrics();
2406
2407        t.out.orig_line_height = config.line_height;
2408        t.out.orig_line_spacing = config.line_spacing;
2409        t.out.line_height = config.line_height;
2410        t.out.line_spacing = config.line_spacing;
2411        t.out.paragraph_spacing = config.paragraph_spacing;
2412        t.out.paragraph_break = config.paragraph_break;
2413
2414        t.line_height = config.line_height.0 as f32;
2415        t.line_spacing = config.line_spacing.0 as f32;
2416        t.paragraph_spacing = config.paragraph_spacing.0 as f32;
2417        t.paragraph_indent = (config.paragraph_indent.0.0 as f32, config.paragraph_indent.1);
2418        let baseline = metrics.ascent + metrics.line_gap / 2.0;
2419
2420        t.out.baseline = t.out.line_height - baseline;
2421        t.out.underline = t.out.baseline + metrics.underline_position;
2422        t.out.underline_descent = t.out.baseline + metrics.descent + Px(1);
2423        t.out.strikethrough = t.out.baseline + metrics.ascent / 3.0;
2424        t.out.overline = t.out.baseline + metrics.ascent;
2425
2426        let dft_line_height = metrics.line_height().0 as f32;
2427        let center_height = (t.line_height - dft_line_height) / 2.0;
2428
2429        t.origin = euclid::point2::<_, ()>(0.0, baseline.0 as f32 + center_height);
2430        if !t.paragraph_indent.1 {
2431            // not invert
2432            t.origin.x = t.paragraph_indent.0;
2433        }
2434
2435        t.max_line_x = 0.0;
2436        if let Some(inline) = config.inline_constraints {
2437            t.first_line_max = inline.first_max.0 as f32;
2438            t.mid_clear_min = inline.mid_clear_min.0 as f32;
2439            t.allow_first_wrap = true;
2440        } else {
2441            t.first_line_max = f32::INFINITY;
2442            t.mid_clear_min = 0.0;
2443            t.allow_first_wrap = false;
2444        }
2445
2446        t.letter_spacing = config.letter_spacing.0 as f32;
2447        t.word_spacing = config.word_spacing.0 as f32;
2448        t.tab_x_advance = config.tab_x_advance.0 as f32;
2449        t.tab_index = fonts[0].space_index();
2450
2451        t.max_width = if config.max_width == Px::MAX {
2452            f32::INFINITY
2453        } else {
2454            config.max_width.0 as f32
2455        };
2456
2457        t.break_words = match config.word_break {
2458            WordBreak::Normal => {
2459                lang!("ch").matches(&config.lang, true, false)
2460                    || lang!("jp").matches(&config.lang, true, false)
2461                    || lang!("ko").matches(&config.lang, true, false)
2462            }
2463            WordBreak::BreakAll => true,
2464            WordBreak::KeepAll => false,
2465        };
2466
2467        if !matches!(config.hyphens, Hyphens::None) && t.max_width.is_finite() && config.obscuring_char.is_none() {
2468            // "hyphen" can be any char and we need the x-advance for the wrap algorithm.
2469            t.hyphen_glyphs = fonts.shape_segment(config.hyphen_char.as_str(), &word_ctx_key, &config.font_features, |s, f| {
2470                (s.clone(), f.clone())
2471            });
2472        }
2473
2474        if let Some(c) = config.obscuring_char {
2475            t.push_obscured_text(fonts, &config.font_features, &mut word_ctx_key, text, c);
2476        } else {
2477            t.push_text(fonts, &config.font_features, &mut word_ctx_key, text);
2478        }
2479
2480        t.out.glyphs.shrink_to_fit();
2481        t.out.clusters.shrink_to_fit();
2482        t.out.segments.0.shrink_to_fit();
2483        t.out.lines.0.shrink_to_fit();
2484        t.out.fonts.0.shrink_to_fit();
2485        t.out.images.shrink_to_fit();
2486
2487        t.out.debug_assert_ranges();
2488        t.out
2489    }
2490
2491    fn push_obscured_text(
2492        &mut self,
2493        fonts: &[Font],
2494        features: &RFontFeatures,
2495        word_ctx_key: &mut WordContextKey,
2496        text: &SegmentedText,
2497        obscuring_char: char,
2498    ) {
2499        if text.is_empty() {
2500            self.push_last_line(text);
2501            self.push_font(&fonts[0]);
2502            return;
2503        }
2504
2505        let (glyphs, font) = fonts.shape_segment(Txt::from_char(obscuring_char).as_str(), word_ctx_key, features, |s, f| {
2506            (s.clone(), f.clone())
2507        });
2508
2509        for (seg, info) in text.iter() {
2510            let mut seg_glyphs = ShapedSegmentData::default();
2511            for (cluster, _) in seg.char_indices() {
2512                let i = seg_glyphs.glyphs.len();
2513                seg_glyphs.glyphs.extend(glyphs.glyphs.iter().copied());
2514                for g in &mut seg_glyphs.glyphs[i..] {
2515                    g.point.0 += seg_glyphs.x_advance;
2516                    g.cluster = cluster as u32;
2517                }
2518                seg_glyphs.x_advance += glyphs.x_advance;
2519            }
2520            self.push_glyphs(&seg_glyphs, self.letter_spacing);
2521            self.push_text_seg(seg, info);
2522        }
2523
2524        self.push_last_line(text);
2525
2526        self.push_font(&font);
2527    }
2528
2529    fn push_text(&mut self, fonts: &[Font], features: &RFontFeatures, word_ctx_key: &mut WordContextKey, text: &SegmentedText) {
2530        static LIG: [&[u8]; 4] = [b"liga", b"clig", b"dlig", b"hlig"];
2531        let ligature_enabled = fonts[0].face().has_ligatures()
2532            && features.iter().any(|f| {
2533                let tag = f.tag.to_bytes();
2534                LIG.iter().any(|l| *l == tag)
2535            });
2536
2537        let push_words_fn = if ligature_enabled {
2538            Self::push_word_segs_with_lig
2539        } else {
2540            Self::push_word_segs
2541        };
2542
2543        // iterate by groups of is_word
2544        let mut info_start = 0;
2545        let mut words_start = None;
2546        for (i, info) in text.segs().iter().enumerate() {
2547            if info.kind.is_word() {
2548                if words_start.is_none() {
2549                    words_start = Some(i);
2550                }
2551            } else {
2552                if let Some(s) = words_start.take() {
2553                    // push is_word group
2554                    push_words_fn(self, fonts, features, word_ctx_key, text, s, i);
2555                }
2556
2557                // push !_is_word
2558                let seg = &text.text()[info_start..info.end];
2559                self.push_single_seg(fonts, features, word_ctx_key, text, seg, *info);
2560            }
2561            info_start = info.end;
2562        }
2563        if let Some(s) = words_start.take() {
2564            // push is_word group
2565            push_words_fn(self, fonts, features, word_ctx_key, text, s, text.segs().len());
2566        }
2567        self.push_last_line(text);
2568
2569        self.push_font(&fonts[0]);
2570    }
2571    fn push_word_segs_with_lig(
2572        &mut self,
2573        fonts: &[Font],
2574        features: &RFontFeatures,
2575        word_ctx_key: &mut WordContextKey,
2576        text: &SegmentedText,
2577        words_start: usize,
2578        words_end: usize,
2579    ) {
2580        let seg_start = if words_start == 0 { 0 } else { text.segs()[words_start - 1].end };
2581        let end_info = text.segs()[words_end - 1];
2582        let seg_end = end_info.end;
2583        let seg = &text.text()[seg_start..seg_end];
2584
2585        if words_end - words_start == 1 {
2586            return self.push_single_seg(fonts, features, word_ctx_key, text, seg, end_info);
2587        }
2588
2589        // check if `is_word` sequence is a ligature that covers more than one word.
2590        let handled = fonts[0].shape_segment(seg, word_ctx_key, features, |shaped_seg| {
2591            let mut cluster_start = 0;
2592            let mut cluster_end = None;
2593            for g in shaped_seg.glyphs.iter() {
2594                if g.index == 0 {
2595                    // top font not used for at least one word in this sequence
2596                    return false;
2597                }
2598                if seg[cluster_start as usize..g.cluster as usize].chars().take(2).count() > 1 {
2599                    cluster_end = Some(g.index);
2600                    break;
2601                }
2602                cluster_start = g.cluster;
2603            }
2604
2605            if cluster_end.is_none() && seg[cluster_start as usize..].chars().take(2).count() > 1 {
2606                cluster_end = Some(seg.len() as u32);
2607            }
2608            if let Some(cluster_end) = cluster_end {
2609                // previous glyph is a ligature, check word boundaries.
2610                let cluster_start_in_txt = seg_start + cluster_start as usize;
2611                let cluster_end_in_txt = seg_start + cluster_end as usize;
2612
2613                let handle = text.segs()[words_start..words_end]
2614                    .iter()
2615                    .any(|info| info.end > cluster_start_in_txt && info.end <= cluster_end_in_txt);
2616
2617                if handle {
2618                    let max_width = self.actual_max_width();
2619                    if self.origin.x + shaped_seg.x_advance > max_width {
2620                        // need wrap
2621                        if shaped_seg.x_advance > max_width {
2622                            // need segment split
2623                            return false;
2624                        }
2625
2626                        self.push_line_break(true, text);
2627                        self.push_glyphs(shaped_seg, self.letter_spacing);
2628                    }
2629                    self.push_glyphs(shaped_seg, self.letter_spacing);
2630                    let mut seg = seg;
2631                    for info in &text.segs()[words_start..words_end] {
2632                        self.push_text_seg(seg, *info);
2633                        seg = "";
2634                    }
2635
2636                    return true;
2637                }
2638            }
2639
2640            false
2641        });
2642
2643        if !handled {
2644            self.push_word_segs(fonts, features, word_ctx_key, text, words_start, words_end);
2645        }
2646    }
2647    fn push_word_segs(
2648        &mut self,
2649        fonts: &[Font],
2650        features: &RFontFeatures,
2651        word_ctx_key: &mut WordContextKey,
2652        text: &SegmentedText,
2653        words_start: usize,
2654        words_end: usize,
2655    ) {
2656        let seg_start = if words_start == 0 { 0 } else { text.segs()[words_start - 1].end };
2657        let end_info = text.segs()[words_end - 1];
2658        let seg_end = end_info.end;
2659        let seg = &text.text()[seg_start..seg_end];
2660
2661        if words_end - words_start == 1 {
2662            return self.push_single_seg(fonts, features, word_ctx_key, text, seg, end_info);
2663        }
2664
2665        let word_segs = move || {
2666            let mut seg_start = seg_start;
2667            (words_start..words_end).map(move |i| {
2668                let info = text.segs()[i];
2669                let seg = &text.text()[seg_start..info.end];
2670                seg_start = info.end;
2671
2672                (seg, info)
2673            })
2674        };
2675
2676        let max_width = self.actual_max_width();
2677        let mut line_break = false;
2678        let mut x_advance = 0.0;
2679        for (seg, info) in word_segs() {
2680            debug_assert!(info.kind.is_word());
2681            word_ctx_key.direction = info.direction();
2682            fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, _| {
2683                x_advance += shaped_seg.x_advance;
2684            });
2685            if self.origin.x + x_advance > max_width {
2686                // need wrap
2687                if x_advance > max_width {
2688                    // need word segments split
2689                    for (seg, info) in word_segs() {
2690                        self.push_single_seg(fonts, features, word_ctx_key, text, seg, info);
2691                    }
2692                    return;
2693                }
2694                // else
2695                line_break = true;
2696            }
2697        }
2698
2699        if line_break {
2700            self.push_line_break(true, text);
2701        }
2702        for (seg, info) in word_segs() {
2703            word_ctx_key.direction = info.direction();
2704            fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2705                self.push_glyphs(shaped_seg, self.letter_spacing);
2706                self.push_text_seg(seg, info);
2707                if matches!(info.kind, TextSegmentKind::Emoji) {
2708                    self.push_emoji_seg(shaped_seg, font);
2709                }
2710                self.push_font(font);
2711            });
2712        }
2713    }
2714    fn push_single_seg(
2715        &mut self,
2716        fonts: &[Font],
2717        features: &RFontFeatures,
2718        word_ctx_key: &mut WordContextKey,
2719        text: &SegmentedText,
2720        seg: &str,
2721        info: TextSegment,
2722    ) {
2723        word_ctx_key.direction = info.direction();
2724        if info.kind.is_word() {
2725            let max_width = self.actual_max_width();
2726
2727            fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2728                if self.origin.x + shaped_seg.x_advance > max_width {
2729                    // need wrap
2730                    if shaped_seg.x_advance > max_width {
2731                        // need segment split
2732
2733                        // try to hyphenate
2734                        let hyphenated = self.push_hyphenate(seg, font, shaped_seg, info, text);
2735
2736                        if !hyphenated && self.break_words {
2737                            // break word
2738                            self.push_split_seg(shaped_seg, seg, info, self.letter_spacing, text);
2739                        } else if !hyphenated {
2740                            let current_start = if self.out.lines.0.is_empty() {
2741                                0
2742                            } else {
2743                                self.out.lines.last().end
2744                            };
2745                            if !self.out.segments.0[current_start..].is_empty() {
2746                                self.push_line_break(true, text);
2747                            } else if current_start == 0 && self.allow_first_wrap {
2748                                self.out.first_wrapped = true;
2749                            }
2750                            self.push_glyphs(shaped_seg, self.letter_spacing);
2751                            self.push_text_seg(seg, info);
2752                        }
2753                    } else {
2754                        self.push_line_break(true, text);
2755                        self.push_glyphs(shaped_seg, self.letter_spacing);
2756                        self.push_text_seg(seg, info);
2757                    }
2758                } else {
2759                    // don't need wrap
2760                    self.push_glyphs(shaped_seg, self.letter_spacing);
2761                    self.push_text_seg(seg, info);
2762                }
2763
2764                if matches!(info.kind, TextSegmentKind::Emoji) {
2765                    self.push_emoji_seg(shaped_seg, font);
2766                }
2767
2768                self.push_font(font);
2769            });
2770        } else if info.kind.is_space() {
2771            if matches!(info.kind, TextSegmentKind::Tab) {
2772                let max_width = self.actual_max_width();
2773                for (i, _) in seg.char_indices() {
2774                    if self.origin.x + self.tab_x_advance > max_width {
2775                        // normal wrap, advance overflow
2776                        self.push_line_break(true, text);
2777                    }
2778                    let point = euclid::point2(self.origin.x, self.origin.y);
2779                    self.origin.x += self.tab_x_advance;
2780                    self.out.glyphs.push(GlyphInstance::new(self.tab_index, point));
2781                    self.out.clusters.push(i as u32);
2782                }
2783
2784                self.push_text_seg(seg, info);
2785                self.push_font(&fonts[0]);
2786            } else {
2787                let max_width = self.actual_max_width();
2788                fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2789                    if self.origin.x + shaped_seg.x_advance > max_width {
2790                        // need wrap
2791                        if seg.len() > 2 {
2792                            // split spaces
2793                            self.push_split_seg(shaped_seg, seg, info, self.word_spacing, text);
2794                        } else {
2795                            self.push_line_break(true, text);
2796                            self.push_glyphs(shaped_seg, self.word_spacing);
2797                            self.push_text_seg(seg, info);
2798                        }
2799                    } else {
2800                        self.push_glyphs(shaped_seg, self.word_spacing);
2801                        self.push_text_seg(seg, info);
2802                    }
2803
2804                    self.push_font(font);
2805                });
2806            }
2807        } else if info.kind.is_line_break() {
2808            self.push_text_seg(seg, info);
2809            self.push_line_break(false, text);
2810        } else {
2811            self.push_text_seg(seg, info)
2812        }
2813    }
2814    fn push_emoji_seg(&mut self, shaped_seg: &ShapedSegmentData, font: &Font) {
2815        if !font.face().color_glyphs().is_empty() {
2816            self.out.has_colored_glyphs = true;
2817        }
2818        if (font.face().has_raster_images() || (cfg!(feature = "svg") && font.face().has_svg_images()))
2819            && let Some(ttf) = font.face().ttf()
2820        {
2821            for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2822                let id = ttf_parser::GlyphId(g.index as _);
2823                let ppm = font.size().0 as u16;
2824                let glyphs_i = self.out.glyphs.len() - shaped_seg.glyphs.len() + i;
2825                if let Some(img) = ttf.glyph_raster_image(id, ppm) {
2826                    self.push_glyph_raster(glyphs_i as _, img);
2827                } else if cfg!(feature = "svg")
2828                    && let Some(img) = ttf.glyph_svg_image(id)
2829                {
2830                    self.push_glyph_svg(glyphs_i as _, img);
2831                }
2832            }
2833        }
2834    }
2835
2836    fn push_glyph_raster(&mut self, glyphs_i: u32, img: ttf_parser::RasterGlyphImage) {
2837        use ttf_parser::RasterImageFormat;
2838        let size = PxSize::new(Px(img.width as _), Px(img.height as _));
2839        let bgra_fmt = ImageDataFormat::Bgra8 {
2840            size,
2841            density: None,
2842            original_color_type: ColorType::BGRA8,
2843        };
2844        let bgra_len = img.width as usize * img.height as usize * 4;
2845        let (data, fmt) = match img.format {
2846            RasterImageFormat::PNG => (img.data.to_vec(), ImageDataFormat::from("png")),
2847            RasterImageFormat::BitmapMono => {
2848                // row aligned 1-bitmap
2849                let mut bgra = Vec::with_capacity(bgra_len);
2850                let bytes_per_row = (img.width as usize).div_ceil(8);
2851                for y in 0..img.height as usize {
2852                    let row_start = y * bytes_per_row;
2853                    for x in 0..img.width as usize {
2854                        let byte_index = row_start + x / 8;
2855                        let bit_index = 7 - (x % 8);
2856                        let bit = (img.data[byte_index] >> bit_index) & 1;
2857                        let color = if bit == 1 { [0, 0, 0, 255] } else { [255, 255, 255, 255] };
2858                        bgra.extend_from_slice(&color);
2859                    }
2860                }
2861                (bgra, bgra_fmt)
2862            }
2863            RasterImageFormat::BitmapMonoPacked => {
2864                // packed 1-bitmap
2865                let mut bgra = Vec::with_capacity(bgra_len);
2866                for &c8 in img.data {
2867                    for bit in 0..8 {
2868                        let color = if (c8 >> (7 - bit)) & 1 == 1 {
2869                            [0, 0, 0, 255]
2870                        } else {
2871                            [255, 255, 255, 255]
2872                        };
2873                        bgra.extend_from_slice(&color);
2874                        if bgra.len() == bgra_len {
2875                            break;
2876                        }
2877                    }
2878                }
2879                (bgra, bgra_fmt)
2880            }
2881            RasterImageFormat::BitmapGray2 => {
2882                // row aligned 2-bitmap
2883                let mut bgra = Vec::with_capacity(bgra_len);
2884                let bytes_per_row = (img.width as usize).div_ceil(4);
2885                for y in 0..img.height as usize {
2886                    let row_start = y * bytes_per_row;
2887                    for x in 0..img.width as usize {
2888                        let byte_index = row_start + x / 4;
2889                        let shift = (3 - (x % 4)) * 2;
2890                        let gray = (img.data[byte_index] >> shift) & 0b11;
2891                        let color = match gray {
2892                            0b00 => [0, 0, 0, 255],       // Black
2893                            0b01 => [85, 85, 85, 255],    // Dark gray
2894                            0b10 => [170, 170, 170, 255], // Light gray
2895                            0b11 => [255, 255, 255, 255], // White
2896                            _ => unreachable!(),
2897                        };
2898                        bgra.extend_from_slice(&color);
2899                    }
2900                }
2901                (bgra, bgra_fmt)
2902            }
2903            RasterImageFormat::BitmapGray2Packed => {
2904                // packed 2-bitmap
2905                let mut bgra = Vec::with_capacity(bgra_len);
2906                for &c4 in img.data {
2907                    for i in 0..4 {
2908                        let gray = (c4 >> (7 - i * 2)) & 0b11;
2909                        let color = match gray {
2910                            0b00 => [0, 0, 0, 255],       // Black
2911                            0b01 => [85, 85, 85, 255],    // Dark gray
2912                            0b10 => [170, 170, 170, 255], // Light gray
2913                            0b11 => [255, 255, 255, 255], // White
2914                            _ => unreachable!(),
2915                        };
2916                        bgra.extend_from_slice(&color);
2917                        if bgra.len() == bgra_len {
2918                            break;
2919                        }
2920                    }
2921                }
2922                (bgra, bgra_fmt)
2923            }
2924            RasterImageFormat::BitmapGray4 => {
2925                // row aligned 4-bitmap
2926                let mut bgra = Vec::with_capacity(bgra_len);
2927                let bytes_per_row = (img.width as usize).div_ceil(2);
2928                for y in 0..img.height as usize {
2929                    let row_start = y * bytes_per_row;
2930                    for x in 0..img.width as usize {
2931                        let byte_index = row_start + x / 2;
2932                        let shift = if x % 2 == 0 { 4 } else { 0 };
2933                        let gray = (img.data[byte_index] >> shift) & 0b1111;
2934                        let g = gray * 17;
2935                        bgra.extend_from_slice(&[g, g, g, 255]);
2936                    }
2937                }
2938                (bgra, bgra_fmt)
2939            }
2940            RasterImageFormat::BitmapGray4Packed => {
2941                let mut bgra = Vec::with_capacity(bgra_len);
2942                for &c2 in img.data {
2943                    for i in 0..2 {
2944                        let gray = (c2 >> (7 - i * 4)) & 0b1111;
2945                        let g = gray * 17;
2946                        bgra.extend_from_slice(&[g, g, g, 255]);
2947                        if bgra.len() == bgra_len {
2948                            break;
2949                        }
2950                    }
2951                }
2952                (bgra, bgra_fmt)
2953            }
2954            RasterImageFormat::BitmapGray8 => {
2955                let mut bgra = Vec::with_capacity(bgra_len);
2956                for &c in img.data {
2957                    bgra.extend_from_slice(&[c, c, c, 255]);
2958                }
2959                (bgra, bgra_fmt)
2960            }
2961            RasterImageFormat::BitmapPremulBgra32 => {
2962                let mut bgra = img.data.to_vec();
2963                for c in bgra.chunks_exact_mut(4) {
2964                    let (b, g, r, a) = (c[0], c[1], c[2], c[3]);
2965                    let unp = if a == 255 {
2966                        [b, g, r]
2967                    } else {
2968                        [
2969                            (b as u32 * 255 / a as u32) as u8,
2970                            (g as u32 * 255 / a as u32) as u8,
2971                            (r as u32 * 255 / a as u32) as u8,
2972                        ]
2973                    };
2974                    c.copy_from_slice(&unp);
2975                }
2976                (bgra, bgra_fmt)
2977            }
2978        };
2979        self.push_glyph_img(glyphs_i, ImageSource::from((data, fmt)));
2980    }
2981
2982    fn push_glyph_svg(&mut self, glyphs_i: u32, img: ttf_parser::svg::SvgDocument) {
2983        self.push_glyph_img(glyphs_i, ImageSource::from((img.data, ImageDataFormat::from("svg"))));
2984    }
2985
2986    fn push_glyph_img(&mut self, glyphs_i: u32, source: ImageSource) {
2987        let img = IMAGES.image(source, ImageOptions::cache(), None);
2988
2989        self.out.images.push((glyphs_i, GlyphImage(img)));
2990    }
2991
2992    fn push_last_line(&mut self, text: &SegmentedText) {
2993        let directions = self.finish_current_line_bidi(text);
2994        self.out.lines.0.push(LineRange {
2995            end: self.out.segments.0.len(),
2996            width: self.origin.x,
2997            x_offset: 0.0,
2998            directions,
2999        });
3000
3001        self.out.update_mid_size();
3002        self.out.update_first_last_lines();
3003        self.out.orig_first_line = self.out.first_line.size;
3004        self.out.orig_last_line = self.out.last_line.size;
3005        if self.out.is_inlined && self.out.lines.0.len() > 1 {
3006            self.out.last_line.origin.y += self.out.mid_clear;
3007        }
3008    }
3009
3010    fn push_hyphenate(&mut self, seg: &str, font: &Font, shaped_seg: &ShapedSegmentData, info: TextSegment, text: &SegmentedText) -> bool {
3011        if !matches!(self.hyphens, Hyphens::Auto) {
3012            return false;
3013        }
3014
3015        let split_points = HYPHENATION.hyphenate(&self.lang, seg);
3016        self.push_hyphenate_pt(&split_points, 0, font, shaped_seg, seg, info, text)
3017    }
3018
3019    #[allow(clippy::too_many_arguments)]
3020    fn push_hyphenate_pt(
3021        &mut self,
3022        split_points: &[usize],
3023        split_points_sub: usize,
3024        font: &Font,
3025        shaped_seg: &ShapedSegmentData,
3026        seg: &str,
3027        info: TextSegment,
3028        text: &SegmentedText,
3029    ) -> bool {
3030        if split_points.is_empty() {
3031            return false;
3032        }
3033
3034        // find the split that fits more letters and hyphen
3035        let mut end_glyph = 0;
3036        let mut end_point_i = 0;
3037        let max_width = self.actual_max_width();
3038        for (i, point) in split_points.iter().enumerate() {
3039            let mut point = *point - split_points_sub;
3040            let mut width = 0.0;
3041            let mut c = u32::MAX;
3042            let mut gi = 0;
3043            // find the first glyph in the cluster at the char byte index `point`
3044            for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3045                width = g.point.0;
3046                if g.cluster != c {
3047                    // advanced cluster, advance point
3048                    if point == 0 {
3049                        break;
3050                    }
3051                    c = g.cluster;
3052                    point -= 1;
3053                }
3054                gi = i;
3055            }
3056
3057            if self.origin.x + width + self.hyphen_glyphs.0.x_advance > max_width {
3058                // fragment+hyphen is to large
3059                if end_glyph == 0 {
3060                    // found no candidate, there is no way to avoid overflow, use smallest option
3061                    end_glyph = gi + 1;
3062                    end_point_i = i + 1;
3063                }
3064                break;
3065            } else {
3066                // found candidate fragment
3067                end_glyph = gi + 1;
3068                end_point_i = i + 1;
3069            }
3070        }
3071
3072        // split and push the first half + hyphen
3073        let end_glyph_x = shaped_seg.glyphs[end_glyph].point.0;
3074        let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3075
3076        if glyphs_a.is_empty() || glyphs_b.is_empty() {
3077            debug_assert!(false, "invalid hyphenation split");
3078            return false;
3079        }
3080        let end_cluster = glyphs_b[0].cluster;
3081        let (seg_a, seg_b) = seg.split_at(end_cluster as usize);
3082
3083        self.push_glyphs(
3084            &ShapedSegmentData {
3085                glyphs: glyphs_a.to_vec(),
3086                x_advance: end_glyph_x,
3087                y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3088            },
3089            self.word_spacing,
3090        );
3091        self.push_font(font);
3092
3093        self.push_glyphs(&self.hyphen_glyphs.0.clone(), 0.0);
3094        self.push_font(&self.hyphen_glyphs.1.clone());
3095
3096        self.push_text_seg(seg_a, info);
3097
3098        self.push_line_break(true, text);
3099
3100        // adjust the second half to a new line
3101        let mut shaped_seg_b = ShapedSegmentData {
3102            glyphs: glyphs_b.to_vec(),
3103            x_advance: shaped_seg.x_advance - end_glyph_x,
3104            y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3105        };
3106        for g in &mut shaped_seg_b.glyphs {
3107            g.point.0 -= end_glyph_x;
3108            g.cluster -= seg_a.len() as u32;
3109        }
3110
3111        if shaped_seg_b.x_advance > self.actual_max_width() {
3112            // second half still does not fit, try to hyphenate again.
3113            if self.push_hyphenate_pt(
3114                &split_points[end_point_i..],
3115                split_points_sub + seg_a.len(),
3116                font,
3117                &shaped_seg_b,
3118                seg_b,
3119                info,
3120                text,
3121            ) {
3122                return true;
3123            }
3124        }
3125
3126        // push second half
3127        self.push_glyphs(&shaped_seg_b, self.word_spacing);
3128        self.push_text_seg(seg_b, info);
3129        true
3130    }
3131
3132    fn push_glyphs(&mut self, shaped_seg: &ShapedSegmentData, spacing: f32) {
3133        self.out.glyphs.extend(shaped_seg.glyphs.iter().map(|gi| {
3134            let r = GlyphInstance::new(gi.index, euclid::point2(gi.point.0 + self.origin.x, gi.point.1 + self.origin.y));
3135            self.origin.x += spacing;
3136            r
3137        }));
3138        self.out.clusters.extend(shaped_seg.glyphs.iter().map(|gi| gi.cluster));
3139
3140        self.origin.x += shaped_seg.x_advance;
3141        self.origin.y += shaped_seg.y_advance;
3142    }
3143
3144    fn push_line_break(&mut self, soft: bool, text: &SegmentedText) {
3145        if self.out.glyphs.is_empty() && self.allow_first_wrap && soft {
3146            self.out.first_wrapped = true;
3147        } else {
3148            let directions = self.finish_current_line_bidi(text);
3149
3150            self.out.lines.0.push(LineRange {
3151                end: self.out.segments.0.len(),
3152                width: self.origin.x,
3153                x_offset: 0.0,
3154                directions,
3155            });
3156
3157            if self.out.lines.0.len() == 1 {
3158                self.out.first_line = PxRect::from_size(PxSize::new(Px(self.origin.x as i32), Px(self.line_height as i32)));
3159
3160                if !self.out.first_wrapped {
3161                    let mid_clear = (self.mid_clear_min - self.line_height).max(0.0).round();
3162                    self.origin.y += mid_clear;
3163                    self.out.mid_clear = Px(mid_clear as i32);
3164                    self.out.mid_offset = mid_clear;
3165                }
3166            }
3167
3168            self.max_line_x = self.origin.x.max(self.max_line_x);
3169
3170            let is_paragraph_break = match self.out.paragraph_break {
3171                ParagraphBreak::None => false,
3172                ParagraphBreak::Line => !soft,
3173            };
3174
3175            self.origin.x = if self.paragraph_indent.1 != is_paragraph_break {
3176                // first_line && !invert_indent || !first_line && invert_indent
3177                self.paragraph_indent.0
3178            } else {
3179                0.0
3180            };
3181
3182            self.origin.y += if is_paragraph_break {
3183                self.paragraph_spacing
3184            } else {
3185                self.line_height + self.line_spacing
3186            };
3187        }
3188    }
3189
3190    #[must_use]
3191    fn finish_current_line_bidi(&mut self, text: &SegmentedText) -> LayoutDirections {
3192        if self.line_has_rtl {
3193            let seg_start = if self.out.lines.0.is_empty() {
3194                0
3195            } else {
3196                self.out.lines.last().end
3197            };
3198
3199            if self.line_has_ltr {
3200                // mixed direction
3201
3202                let line_segs = seg_start..self.out.segments.0.len();
3203
3204                // compute visual order and offset segments.
3205                let mut x = 0.0;
3206                for i in text.reorder_line_to_ltr(line_segs) {
3207                    let g_range = self.out.segments.glyphs(i);
3208                    if g_range.iter().is_empty() {
3209                        continue;
3210                    }
3211
3212                    let glyphs = &mut self.out.glyphs[g_range.iter()];
3213                    let offset = x - self.out.segments.0[i].x;
3214                    self.out.segments.0[i].x = x;
3215                    for g in glyphs {
3216                        g.point.x += offset;
3217                    }
3218                    x += self.out.segments.0[i].advance;
3219                }
3220            } else {
3221                // entire line RTL
3222                let line_width = self.origin.x;
3223
3224                let mut x = line_width;
3225                for i in seg_start..self.out.segments.0.len() {
3226                    x -= self.out.segments.0[i].advance;
3227
3228                    let g_range = self.out.segments.glyphs(i);
3229
3230                    let glyphs = &mut self.out.glyphs[g_range.iter()];
3231                    let offset = x - self.out.segments.0[i].x;
3232                    self.out.segments.0[i].x = x;
3233                    for g in glyphs {
3234                        g.point.x += offset;
3235                    }
3236                }
3237            }
3238        }
3239
3240        let mut d = LayoutDirections::empty();
3241        d.set(LayoutDirections::LTR, self.line_has_ltr);
3242        d.set(LayoutDirections::RTL, self.line_has_rtl);
3243
3244        self.line_has_ltr = false;
3245        self.line_has_rtl = false;
3246
3247        d
3248    }
3249
3250    pub fn push_text_seg(&mut self, seg: &str, info: TextSegment) {
3251        let g_len = if let Some(l) = self.out.segments.0.last() {
3252            self.out.glyphs.len() - l.end
3253        } else {
3254            self.out.glyphs.len()
3255        };
3256        if g_len > 0 {
3257            self.line_has_ltr |= info.level.is_ltr();
3258            self.line_has_rtl |= info.level.is_rtl();
3259        }
3260
3261        self.text_seg_end += seg.len();
3262
3263        let is_first_of_line =
3264            (!self.out.lines.0.is_empty() && self.out.lines.last().end == self.out.segments.0.len()) || self.out.segments.0.is_empty();
3265        let x = if is_first_of_line {
3266            0.0
3267        } else {
3268            // not first segment of line
3269            self.out.segments.0.last().map(|s| s.x + s.advance).unwrap_or(0.0)
3270        };
3271        self.out.segments.0.push(GlyphSegment {
3272            text: TextSegment {
3273                end: self.text_seg_end,
3274                ..info
3275            },
3276            end: self.out.glyphs.len(),
3277            x,
3278            advance: self.origin.x - x,
3279        });
3280    }
3281
3282    pub fn push_split_seg(&mut self, shaped_seg: &ShapedSegmentData, seg: &str, info: TextSegment, spacing: f32, text: &SegmentedText) {
3283        let mut end_glyph = 0;
3284        let mut end_glyph_x = 0.0;
3285        let max_width = self.actual_max_width();
3286        for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3287            if self.origin.x + g.point.0 > max_width {
3288                break;
3289            }
3290            end_glyph = i;
3291            end_glyph_x = g.point.0;
3292        }
3293
3294        let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3295
3296        if glyphs_a.is_empty() || glyphs_b.is_empty() {
3297            // failed split
3298            self.push_line_break(true, text);
3299            self.push_glyphs(shaped_seg, spacing);
3300            self.push_text_seg(seg, info);
3301        } else {
3302            let (seg_a, seg_b) = seg.split_at(glyphs_b[0].cluster as usize);
3303
3304            let shaped_seg_a = ShapedSegmentData {
3305                glyphs: glyphs_a.to_vec(),
3306                x_advance: end_glyph_x,
3307                y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3308            };
3309            self.push_glyphs(&shaped_seg_a, spacing);
3310            self.push_text_seg(seg_a, info);
3311            self.push_line_break(true, text);
3312
3313            let mut shaped_seg_b = ShapedSegmentData {
3314                glyphs: glyphs_b.to_vec(),
3315                x_advance: shaped_seg.x_advance - end_glyph_x,
3316                y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3317            };
3318            for g in &mut shaped_seg_b.glyphs {
3319                g.point.0 -= shaped_seg_a.x_advance;
3320                g.cluster -= seg_a.len() as u32;
3321            }
3322
3323            if shaped_seg_b.x_advance <= max_width {
3324                self.push_glyphs(&shaped_seg_b, spacing);
3325                self.push_text_seg(seg_b, info);
3326            } else {
3327                self.push_split_seg(&shaped_seg_b, seg_b, info, spacing, text);
3328            }
3329        }
3330    }
3331
3332    fn push_font(&mut self, font: &Font) {
3333        if let Some(last) = self.out.fonts.0.last_mut() {
3334            if &last.font == font {
3335                last.end = self.out.glyphs.len();
3336                return;
3337            } else if last.end == self.out.glyphs.len() {
3338                return;
3339            }
3340        }
3341        self.out.fonts.0.push(FontRange {
3342            font: font.clone(),
3343            end: self.out.glyphs.len(),
3344        })
3345    }
3346}
3347
3348bitflags! {
3349    /// Identifies what direction segments a [`ShapedLine`] has.
3350    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3351    #[serde(transparent)]
3352    pub struct LayoutDirections: u8 {
3353        /// Line has left-to-right segments.
3354        const LTR = 1;
3355        /// Line has right-to-left segments.
3356        const RTL = 2;
3357        /// Line as both left-to-right and right-to-left segments.
3358        ///
3359        /// When this is the case the line segments positions may be re-ordered.
3360        const BIDI = Self::LTR.bits() | Self::RTL.bits();
3361    }
3362}
3363
3364/// Represents a line selection of a [`ShapedText`].
3365#[derive(Clone, Copy)]
3366pub struct ShapedLine<'a> {
3367    text: &'a ShapedText,
3368    // range of segments of this line.
3369    seg_range: IndexRange,
3370    index: usize,
3371    width: Px,
3372}
3373impl fmt::Debug for ShapedLine<'_> {
3374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3375        f.debug_struct("ShapedLine")
3376            .field("seg_range", &self.seg_range)
3377            .field("index", &self.index)
3378            .field("width", &self.width)
3379            .finish_non_exhaustive()
3380    }
3381}
3382impl<'a> ShapedLine<'a> {
3383    /// Height of the line.
3384    pub fn height(&self) -> Px {
3385        if self.index == 0 {
3386            self.text.first_line.height()
3387        } else if self.index == self.text.lines.0.len() - 1 {
3388            self.text.last_line.height()
3389        } else {
3390            self.text.line_height
3391        }
3392    }
3393
3394    /// Width of the line.
3395    pub fn width(&self) -> Px {
3396        if self.index == 0 {
3397            self.text.first_line.width()
3398        } else if self.index == self.text.lines.0.len() - 1 {
3399            self.text.last_line.width()
3400        } else {
3401            self.width
3402        }
3403    }
3404
3405    /// Bounds of the line.
3406    pub fn rect(&self) -> PxRect {
3407        if self.index == 0 {
3408            return self.text.first_line;
3409        }
3410        if self.index == self.text.lines.0.len() - 1 {
3411            return self.text.last_line;
3412        }
3413
3414        let size = PxSize::new(self.width, self.text.line_height);
3415        let origin = PxPoint::new(
3416            Px(self.text.lines.0[self.index].x_offset as i32),
3417            self.text.line_height * Px((self.index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear,
3418        );
3419        PxRect::new(origin, size)
3420    }
3421
3422    /// Initial size of the line, before any line reshaping.
3423    ///
3424    /// This can be different then the current [`rect`] size if the parent inline changed the size, usually to inject
3425    /// blank spaces to justify the text or to visually insert a bidirectional fragment of another widget.
3426    ///
3427    /// [`rect`]: Self::rect
3428    pub fn original_size(&self) -> PxSize {
3429        if self.index == 0 {
3430            return self.text.orig_first_line;
3431        }
3432        if self.index == self.text.lines.0.len() - 1 {
3433            return self.text.orig_last_line;
3434        }
3435        PxSize::new(self.width, self.text.line_height)
3436    }
3437
3438    /// Gets if this line is the first of a paragraph.
3439    ///
3440    /// Paragraphs are defined by the [`paragraph_break`] value.
3441    ///
3442    /// [`paragraph_break`]: ShapedText::paragraph_break
3443    pub fn is_paragraph_start(&self) -> bool {
3444        match self.text.paragraph_break {
3445            ParagraphBreak::None => self.index == 0,
3446            ParagraphBreak::Line => !self.started_by_wrap(),
3447        }
3448    }
3449
3450    /// Gets if this line is the last of a paragraph.
3451    ///
3452    /// Paragraphs are defined by the [`paragraph_break`] value.
3453    ///
3454    /// [`paragraph_break`]: ShapedText::paragraph_break
3455    pub fn is_paragraph_end(&self) -> bool {
3456        match self.text.paragraph_break {
3457            ParagraphBreak::None => self.index == self.text.lines.0.len() - 1,
3458            ParagraphBreak::Line => !self.ended_by_wrap(),
3459        }
3460    }
3461
3462    /// Full overline, start point + width.
3463    pub fn overline(&self) -> (PxPoint, Px) {
3464        self.decoration_line(self.text.overline)
3465    }
3466
3467    /// Full strikethrough line, start point + width.
3468    pub fn strikethrough(&self) -> (PxPoint, Px) {
3469        self.decoration_line(self.text.strikethrough)
3470    }
3471
3472    /// Full underline, not skipping.
3473    ///
3474    /// The *y* is defined by the font metrics.
3475    ///
3476    /// Returns start point + width.
3477    pub fn underline(&self) -> (PxPoint, Px) {
3478        self.decoration_line(self.text.underline)
3479    }
3480
3481    /// Full underline, not skipping.
3482    ///
3483    /// The *y* is the baseline + descent + 1px.
3484    ///
3485    /// Returns start point + width.
3486    pub fn underline_descent(&self) -> (PxPoint, Px) {
3487        self.decoration_line(self.text.underline_descent)
3488    }
3489
3490    /// Underline, skipping spaces.
3491    ///
3492    /// The *y* is defined by the font metrics.
3493    ///
3494    /// Returns and iterator of start point + width for each word.
3495    pub fn underline_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3496        MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline()))
3497    }
3498
3499    /// Underline, skipping spaces.
3500    ///
3501    /// The *y* is the baseline + descent + 1px.
3502    ///
3503    /// Returns and iterator of start point + width for each word.
3504    pub fn underline_descent_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3505        MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline_descent()))
3506    }
3507
3508    /// Underline, skipping glyph descends that intersect the underline.
3509    ///
3510    /// The *y* is defined by the font metrics.
3511    ///
3512    /// Returns an iterator of start point + width for continuous underline.
3513    pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3514        MergingLineIter::new(self.segs().flat_map(move |s| s.underline_skip_glyphs(thickness)))
3515    }
3516
3517    /// Underline, skipping spaces and glyph descends that intersect the underline
3518    ///
3519    /// The *y* is defined by font metrics.
3520    ///
3521    /// Returns an iterator of start point + width for continuous underline.
3522    pub fn underline_skip_glyphs_and_spaces(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3523        MergingLineIter::new(
3524            self.segs()
3525                .filter(|s| s.kind().is_word())
3526                .flat_map(move |s| s.underline_skip_glyphs(thickness)),
3527        )
3528    }
3529
3530    fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3531        let r = self.rect();
3532        let y = r.max_y() - bottom_up_offset;
3533        (PxPoint::new(r.origin.x, y), self.width)
3534    }
3535
3536    fn segments(&self) -> &'a [GlyphSegment] {
3537        &self.text.segments.0[self.seg_range.iter()]
3538    }
3539
3540    /// Glyphs in the line.
3541    ///
3542    /// The glyphs are in text order by segments and in visual order (LTR) within segments, so
3543    /// the RTL text "لما " will have the space glyph first, then "’álif", "miim", "láam".
3544    ///
3545    /// All glyph points are set as offsets to the top-left of the text full text.
3546    pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a {
3547        let r = self.glyphs_range();
3548        self.text.glyphs_range(r)
3549    }
3550
3551    /// Glyphs in the line paired with the *x-advance*.
3552    pub fn glyphs_with_x_advance(&self) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + 'a)> + 'a {
3553        self.segs().flat_map(|s| s.glyphs_with_x_advance())
3554    }
3555
3556    fn glyphs_range(&self) -> IndexRange {
3557        self.text.segments.glyphs_range(self.seg_range)
3558    }
3559
3560    /// Iterate over word and space segments in this line.
3561    pub fn segs(&self) -> impl DoubleEndedIterator<Item = ShapedSegment<'a>> + ExactSizeIterator + use<'a> {
3562        let text = self.text;
3563        let line_index = self.index;
3564        self.seg_range.iter().map(move |i| ShapedSegment {
3565            text,
3566            line_index,
3567            index: i,
3568        })
3569    }
3570
3571    /// Number of segments in this line.
3572    pub fn segs_len(&self) -> usize {
3573        self.seg_range.len()
3574    }
3575
3576    /// Get the segment by index.
3577    ///
3578    /// The first segment of the line is `0`.
3579    pub fn seg(&self, seg_idx: usize) -> Option<ShapedSegment<'_>> {
3580        if self.seg_range.len() > seg_idx {
3581            Some(ShapedSegment {
3582                text: self.text,
3583                line_index: self.index,
3584                index: seg_idx + self.seg_range.start(),
3585            })
3586        } else {
3587            None
3588        }
3589    }
3590
3591    /// Returns `true` if this line was started by the wrap algorithm.
3592    ///
3593    /// If this is `false` then the line is the first or the previous line ends in a [`LineBreak`].
3594    ///
3595    /// [`LineBreak`]: TextSegmentKind::LineBreak
3596    pub fn started_by_wrap(&self) -> bool {
3597        self.index > 0 && {
3598            let prev_line = self.text.lines.segs(self.index - 1);
3599            self.text.segments.0[prev_line.iter()]
3600                .last()
3601                .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3602                .unwrap() // only last line can be empty
3603        }
3604    }
3605
3606    /// Returns `true` if this line was ended by the wrap algorithm.
3607    ///
3608    /// If this is `false` then the line is the last or ends in a [`LineBreak`].
3609    ///
3610    /// [`LineBreak`]: TextSegmentKind::LineBreak
3611    pub fn ended_by_wrap(&self) -> bool {
3612        // not last and not ended in line-break.
3613        self.index < self.text.lines.0.len() - 1
3614            && self
3615                .segments()
3616                .last()
3617                .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3618                .unwrap() // only last line can be empty
3619    }
3620
3621    /// Returns the line or first previous line that is not [`started_by_wrap`].
3622    ///
3623    /// [`started_by_wrap`]: Self::started_by_wrap
3624    pub fn actual_line_start(&self) -> Self {
3625        let mut r = *self;
3626        while r.started_by_wrap() {
3627            r = r.text.line(r.index - 1).unwrap();
3628        }
3629        r
3630    }
3631
3632    /// Returns the line or first next line that is not [`ended_by_wrap`].
3633    ///
3634    /// [`ended_by_wrap`]: Self::ended_by_wrap
3635    pub fn actual_line_end(&self) -> Self {
3636        let mut r = *self;
3637        while r.ended_by_wrap() {
3638            r = r.text.line(r.index + 1).unwrap();
3639        }
3640        r
3641    }
3642
3643    /// Get the text bytes range of this line in the original text.
3644    pub fn text_range(&self) -> ops::Range<usize> {
3645        let start = self.seg_range.start();
3646        let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3647        let end = self.seg_range.end();
3648        let end = if end == 0 { 0 } else { self.text.segments.0[end - 1].text.end };
3649
3650        start..end
3651    }
3652
3653    /// Get the text bytes range of this line in the original text, excluding the line break
3654    /// to keep [`end`] in the same line.
3655    ///
3656    /// [`end`]: ops::Range<usize>::end
3657    pub fn text_caret_range(&self) -> ops::Range<usize> {
3658        let start = self.seg_range.start();
3659        let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3660        let end = self.seg_range.end();
3661        let end = if end == 0 {
3662            0
3663        } else if self.seg_range.start() == end {
3664            start
3665        } else {
3666            let seg = &self.text.segments.0[end - 1];
3667            if !matches!(seg.text.kind, TextSegmentKind::LineBreak) {
3668                seg.text.end
3669            } else {
3670                // start of LineBreak segment
3671                if end == 1 { 0 } else { self.text.segments.0[end - 2].text.end }
3672            }
3673        };
3674
3675        start..end
3676    }
3677
3678    /// Gets the text range of the actual line, joining shaped lines that are started by wrap.
3679    pub fn actual_text_range(&self) -> ops::Range<usize> {
3680        let start = self.actual_line_start().text_range().start;
3681        let end = self.actual_line_end().text_range().end;
3682        start..end
3683    }
3684
3685    /// Gets the text range of the actual line, excluding the line break at the end.
3686    pub fn actual_text_caret_range(&self) -> ops::Range<usize> {
3687        let start = self.actual_line_start().text_range().start;
3688        let end = self.actual_line_end().text_caret_range().end;
3689        start..end
3690    }
3691
3692    /// Select the string represented by this line.
3693    ///
3694    /// The `full_text` must be equal to the original text that was used to generate the parent [`ShapedText`].
3695    pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
3696        let r = self.text_range();
3697
3698        let start = r.start.min(full_text.len());
3699        let end = r.end.min(full_text.len());
3700
3701        &full_text[start..end]
3702    }
3703
3704    /// Gets the segment that contains `x` or is nearest to it.
3705    pub fn nearest_seg(&self, x: Px) -> Option<ShapedSegment<'a>> {
3706        let mut min = None;
3707        let mut min_dist = Px::MAX;
3708        for seg in self.segs() {
3709            let (seg_x, width) = seg.x_width();
3710            if x >= seg_x {
3711                let seg_max_x = seg_x + width;
3712                if x < seg_max_x {
3713                    return Some(seg);
3714                }
3715            }
3716            let dist = (x - seg_x).abs();
3717            if min_dist > dist {
3718                min = Some(seg);
3719                min_dist = dist;
3720            }
3721        }
3722        min
3723    }
3724
3725    /// Gets the line index.
3726    pub fn index(&self) -> usize {
3727        self.index
3728    }
3729
3730    /// Layout directions of segments in this line.
3731    pub fn directions(&self) -> LayoutDirections {
3732        self.text.lines.0[self.index].directions
3733    }
3734}
3735
3736/// Merges lines defined by `(PxPoint, Px)`, assuming the `y` is equal.
3737struct MergingLineIter<I> {
3738    iter: I,
3739    line: Option<(PxPoint, Px)>,
3740}
3741impl<I> MergingLineIter<I> {
3742    pub fn new(iter: I) -> Self {
3743        MergingLineIter { iter, line: None }
3744    }
3745}
3746impl<I: Iterator<Item = (PxPoint, Px)>> Iterator for MergingLineIter<I> {
3747    type Item = I::Item;
3748
3749    fn next(&mut self) -> Option<Self::Item> {
3750        loop {
3751            match self.iter.next() {
3752                Some(line) => {
3753                    if let Some(prev_line) = &mut self.line {
3754                        fn min_x((origin, _width): (PxPoint, Px)) -> Px {
3755                            origin.x
3756                        }
3757                        fn max_x((origin, width): (PxPoint, Px)) -> Px {
3758                            origin.x + width
3759                        }
3760
3761                        if prev_line.0.y == line.0.y && min_x(*prev_line) <= max_x(line) && max_x(*prev_line) >= min_x(line) {
3762                            let x = min_x(*prev_line).min(min_x(line));
3763                            prev_line.1 = max_x(*prev_line).max(max_x(line)) - x;
3764                            prev_line.0.x = x;
3765                        } else {
3766                            let cut = mem::replace(prev_line, line);
3767                            return Some(cut);
3768                        }
3769                    } else {
3770                        self.line = Some(line);
3771                        continue;
3772                    }
3773                }
3774                None => return self.line.take(),
3775            }
3776        }
3777    }
3778}
3779
3780struct MergingRectIter<I> {
3781    iter: I,
3782    rect: Option<PxBox>,
3783}
3784impl<I> MergingRectIter<I> {
3785    pub fn new(iter: I) -> Self {
3786        MergingRectIter { iter, rect: None }
3787    }
3788}
3789impl<I: Iterator<Item = PxRect>> Iterator for MergingRectIter<I> {
3790    type Item = I::Item;
3791
3792    fn next(&mut self) -> Option<Self::Item> {
3793        loop {
3794            match self.iter.next() {
3795                Some(rect) => {
3796                    let rect = rect.to_box2d();
3797                    if let Some(prev_rect) = &mut self.rect {
3798                        if prev_rect.min.y == rect.min.y
3799                            && prev_rect.max.y == rect.max.y
3800                            && prev_rect.min.x <= rect.max.x
3801                            && prev_rect.max.x >= rect.min.x
3802                        {
3803                            prev_rect.min.x = prev_rect.min.x.min(rect.min.x);
3804                            prev_rect.max.x = prev_rect.max.x.max(rect.max.x);
3805                            continue;
3806                        } else {
3807                            let cut = mem::replace(prev_rect, rect);
3808                            return Some(cut.to_rect());
3809                        }
3810                    } else {
3811                        self.rect = Some(rect);
3812                        continue;
3813                    }
3814                }
3815                None => return self.rect.take().map(|r| r.to_rect()),
3816            }
3817        }
3818    }
3819}
3820
3821/// Represents a word or space selection of a [`ShapedText`].
3822#[derive(Clone, Copy)]
3823pub struct ShapedSegment<'a> {
3824    text: &'a ShapedText,
3825    line_index: usize,
3826    index: usize,
3827}
3828impl fmt::Debug for ShapedSegment<'_> {
3829    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3830        f.debug_struct("ShapedSegment")
3831            .field("line_index", &self.line_index)
3832            .field("index", &self.index)
3833            .finish_non_exhaustive()
3834    }
3835}
3836impl<'a> ShapedSegment<'a> {
3837    /// Segment kind.
3838    pub fn kind(&self) -> TextSegmentKind {
3839        self.text.segments.0[self.index].text.kind
3840    }
3841
3842    /// Segment bidi level.
3843    pub fn level(&self) -> BidiLevel {
3844        self.text.segments.0[self.index].text.level
3845    }
3846
3847    /// Layout direction of glyphs in the segment.
3848    pub fn direction(&self) -> LayoutDirection {
3849        self.text.segments.0[self.index].text.direction()
3850    }
3851
3852    /// If the segment contains the last glyph of the line.
3853    pub fn has_last_glyph(&self) -> bool {
3854        let seg_glyphs = self.text.segments.glyphs(self.index);
3855        let s = self.text.lines.segs(self.line_index);
3856        let line_glyphs = self.text.segments.glyphs_range(s);
3857        seg_glyphs.end() == line_glyphs.end()
3858    }
3859
3860    fn glyphs_range(&self) -> IndexRange {
3861        self.text.segments.glyphs(self.index)
3862    }
3863
3864    /// Glyphs in the word or space.
3865    ///
3866    /// The glyphs are in visual order (LTR) within segments, so
3867    /// the RTL text "لما" will yield "’álif", "miim", "láam".
3868    ///
3869    /// All glyph points are set as offsets to the top-left of the text full text.
3870    ///
3871    /// Note that multiple glyphs can map to the same char and multiple chars can map to the same glyph, you can use the [`clusters`]
3872    /// map to find the char for each glyph. Some font ligatures also bridge multiple segments, in this case only the first shaped
3873    /// segment has glyphs the subsequent ones are empty.
3874    ///
3875    /// [`clusters`]: Self::clusters
3876    pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> {
3877        let r = self.glyphs_range();
3878        self.text.glyphs_range(r)
3879    }
3880
3881    /// Gets the specific glyph and font.
3882    pub fn glyph(&self, index: usize) -> Option<(&'a Font, GlyphInstance)> {
3883        let mut r = self.glyphs_range();
3884        r.0 += index;
3885        self.text.glyphs_range(r).next().map(|(f, g)| (f, g[0]))
3886    }
3887
3888    /// Map glyph -> char.
3889    ///
3890    /// Each [`glyphs`] glyph pairs with an entry in this slice that is the char byte index in [`text`]. If
3891    /// a font ligature bridges multiple segments only the first segment will have a non-empty map.
3892    ///
3893    /// [`glyphs`]: Self::glyphs
3894    /// [`text`]: Self::text
3895    pub fn clusters(&self) -> &[u32] {
3896        let r = self.glyphs_range();
3897        self.text.clusters_range(r)
3898    }
3899
3900    /// Count the deduplicated [`clusters`].
3901    ///
3902    /// [`clusters`]: Self::clusters
3903    pub fn clusters_count(&self) -> usize {
3904        let mut c = u32::MAX;
3905        let mut count = 0;
3906        for &i in self.clusters() {
3907            if i != c {
3908                c = i;
3909                count += 1;
3910            }
3911        }
3912        count
3913    }
3914
3915    /// Number of next segments that are empty because their text is included in a ligature
3916    /// glyph or glyphs started in this segment.
3917    pub fn ligature_segs_count(&self) -> usize {
3918        let range = self.glyphs_range();
3919        if range.iter().is_empty() {
3920            0
3921        } else {
3922            self.text.segments.0[self.index + 1..]
3923                .iter()
3924                .filter(|s| s.end == range.end())
3925                .count()
3926        }
3927    }
3928
3929    /// Glyphs in the segment, paired with the *x-advance*.
3930    ///
3931    /// Yields `(Font, [(glyph, advance)])`.
3932    pub fn glyphs_with_x_advance(
3933        &self,
3934    ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + use<'a>)> + use<'a> {
3935        let r = self.glyphs_range();
3936        self.text.seg_glyphs_with_x_advance(self.index, r)
3937    }
3938
3939    /// Glyphs per cluster in the segment, paired with the *x-advance* of the cluster.
3940    ///
3941    /// Yields `(Font, [(cluster, [glyph], advance)])`.
3942    pub fn cluster_glyphs_with_x_advance(
3943        &self,
3944    ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (u32, &'a [GlyphInstance], f32)> + use<'a>)> + use<'a> {
3945        let r = self.glyphs_range();
3946        self.text.seg_cluster_glyphs_with_x_advance(self.index, r)
3947    }
3948
3949    /// Gets the segment x offset and advance.
3950    pub fn x_width(&self) -> (Px, Px) {
3951        let IndexRange(start, end) = self.glyphs_range();
3952
3953        let is_line_break = start == end && matches!(self.kind(), TextSegmentKind::LineBreak);
3954
3955        let start_x = match self.direction() {
3956            LayoutDirection::LTR => {
3957                if is_line_break || start == self.text.glyphs.len() {
3958                    let x = self.text.lines.x_offset(self.line_index);
3959                    let w = self.text.lines.width(self.line_index);
3960                    return (Px((x + w) as i32), Px(0));
3961                }
3962                self.text.glyphs[start].point.x
3963            }
3964            LayoutDirection::RTL => {
3965                if is_line_break || start == self.text.glyphs.len() {
3966                    let x = self.text.lines.x_offset(self.line_index);
3967                    return (Px(x as i32), Px(0));
3968                }
3969
3970                self.text.glyphs[start..end]
3971                    .iter()
3972                    .map(|g| g.point.x)
3973                    .min_by(f32::total_cmp)
3974                    .unwrap_or(0.0)
3975            }
3976        };
3977
3978        (Px(start_x.floor() as i32), Px(self.advance().ceil() as i32))
3979    }
3980
3981    /// Segment exact *width* in pixels.
3982    pub fn advance(&self) -> f32 {
3983        self.text.segments.0[self.index].advance
3984    }
3985
3986    /// Bounds of the word or spaces.
3987    pub fn rect(&self) -> PxRect {
3988        let (x, width) = self.x_width();
3989        let size = PxSize::new(width, self.text.line_height);
3990
3991        let y = if self.line_index == 0 {
3992            self.text.first_line.origin.y
3993        } else if self.line_index == self.text.lines.0.len() - 1 {
3994            self.text.last_line.origin.y
3995        } else {
3996            self.text.line_height * Px((self.line_index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear
3997        };
3998        PxRect::new(PxPoint::new(x, y), size)
3999    }
4000
4001    /// Gets the first char and glyph with advance that overflows `max_width`.
4002    pub fn overflow_char_glyph(&self, max_width_px: f32) -> Option<(usize, usize)> {
4003        if self.advance() > max_width_px {
4004            match self.direction() {
4005                LayoutDirection::LTR => {
4006                    let mut x = 0.0;
4007                    let mut g = 0;
4008                    for (_, c) in self.cluster_glyphs_with_x_advance() {
4009                        for (cluster, glyphs, advance) in c {
4010                            x += advance;
4011                            if x > max_width_px {
4012                                return Some((cluster as usize, g));
4013                            }
4014                            g += glyphs.len();
4015                        }
4016                    }
4017                }
4018                LayoutDirection::RTL => {
4019                    let mut g = 0;
4020                    let mut rev = smallvec::SmallVec::<[_; 10]>::new();
4021                    for (_, c) in self.cluster_glyphs_with_x_advance() {
4022                        for (cluster, glyphs, advance) in c {
4023                            rev.push((cluster, g, advance));
4024                            g += glyphs.len();
4025                        }
4026                    }
4027
4028                    let mut x = 0.0;
4029                    for (c, g, advance) in rev.into_iter().rev() {
4030                        x += advance;
4031                        if x > max_width_px {
4032                            return Some((c as usize, g));
4033                        }
4034                    }
4035                }
4036            }
4037        }
4038        None
4039    }
4040
4041    /// Segment info for widget inline segments.
4042    pub fn inline_info(&self) -> InlineSegmentInfo {
4043        let (x, width) = self.x_width();
4044        InlineSegmentInfo::new(x, width)
4045    }
4046
4047    fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
4048        let (x, width) = self.x_width();
4049        let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - bottom_up_offset;
4050        (PxPoint::new(x, y), width)
4051    }
4052
4053    /// Overline spanning the word or spaces, start point + width.
4054    pub fn overline(&self) -> (PxPoint, Px) {
4055        self.decoration_line(self.text.overline)
4056    }
4057
4058    /// Strikethrough spanning the word or spaces, start point + width.
4059    pub fn strikethrough(&self) -> (PxPoint, Px) {
4060        self.decoration_line(self.text.strikethrough)
4061    }
4062
4063    /// Underline spanning the word or spaces, not skipping.
4064    ///
4065    /// The *y* is defined by the font metrics.
4066    ///
4067    /// Returns start point + width.
4068    pub fn underline(&self) -> (PxPoint, Px) {
4069        self.decoration_line(self.text.underline)
4070    }
4071
4072    /// Underline spanning the word or spaces, skipping glyph descends that intercept the line.
4073    ///
4074    /// Returns an iterator of start point + width for underline segments.
4075    pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + use<'a> {
4076        let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - self.text.underline;
4077        let (x, _) = self.x_width();
4078
4079        let line_y = -(self.text.baseline - self.text.underline).0 as f32;
4080        let line_y_range = (line_y, line_y - thickness.0 as f32);
4081
4082        // space around glyph descends, thickness clamped to a minimum of 1px and a maximum of 0.2em (same as Firefox).
4083        let padding = (thickness.0 as f32).clamp(1.0, (self.text.fonts.font(0).size().0 as f32 * 0.2).max(1.0));
4084
4085        // no yield, only sadness
4086        struct UnderlineSkipGlyphs<'a, I, J> {
4087            line_y_range: (f32, f32),
4088            y: Px,
4089            padding: f32,
4090            min_width: Px,
4091
4092            iter: I,
4093            resume: Option<(&'a Font, J)>,
4094            x: f32,
4095            width: f32,
4096        }
4097        impl<I, J> UnderlineSkipGlyphs<'_, I, J> {
4098            fn line(&self) -> Option<(PxPoint, Px)> {
4099                fn f32_to_px(px: f32) -> Px {
4100                    Px(px.round() as i32)
4101                }
4102                let r = (PxPoint::new(f32_to_px(self.x), self.y), f32_to_px(self.width));
4103                if r.1 >= self.min_width { Some(r) } else { None }
4104            }
4105        }
4106        impl<'a, I, J> Iterator for UnderlineSkipGlyphs<'a, I, J>
4107        where
4108            I: Iterator<Item = (&'a Font, J)>,
4109            J: Iterator<Item = (GlyphInstance, f32)>,
4110        {
4111            type Item = (PxPoint, Px);
4112
4113            fn next(&mut self) -> Option<Self::Item> {
4114                loop {
4115                    let continuation = self.resume.take().or_else(|| self.iter.next());
4116                    if let Some((font, mut glyphs_with_adv)) = continuation {
4117                        for (g, a) in &mut glyphs_with_adv {
4118                            if let Some((ex_start, ex_end)) = font.h_line_hits(g.index, self.line_y_range) {
4119                                self.width += ex_start - self.padding;
4120                                let r = self.line();
4121                                self.x += self.width + self.padding + ex_end + self.padding;
4122                                self.width = a - (ex_start + ex_end) - self.padding;
4123
4124                                if r.is_some() {
4125                                    self.resume = Some((font, glyphs_with_adv));
4126                                    return r;
4127                                }
4128                            } else {
4129                                self.width += a;
4130                                // continue
4131                            }
4132                        }
4133                    } else {
4134                        let r = self.line();
4135                        self.width = 0.0;
4136                        return r;
4137                    }
4138                }
4139            }
4140        }
4141        UnderlineSkipGlyphs {
4142            line_y_range,
4143            y,
4144            padding,
4145            min_width: Px((padding / 2.0).max(1.0).ceil() as i32),
4146
4147            iter: self.glyphs_with_x_advance(),
4148            resume: None,
4149            x: x.0 as f32,
4150            width: 0.0,
4151        }
4152    }
4153
4154    /// Underline spanning the word or spaces, not skipping.
4155    ///
4156    /// The *y* is the baseline + descent + 1px.
4157    ///
4158    /// Returns start point + width.
4159    pub fn underline_descent(&self) -> (PxPoint, Px) {
4160        self.decoration_line(self.text.underline_descent)
4161    }
4162
4163    /// Get the text bytes range of this segment in the original text.
4164    pub fn text_range(&self) -> ops::Range<usize> {
4165        self.text_start()..self.text_end()
4166    }
4167
4168    /// Get the text byte range start of this segment in the original text.
4169    pub fn text_start(&self) -> usize {
4170        if self.index == 0 {
4171            0
4172        } else {
4173            self.text.segments.0[self.index - 1].text.end
4174        }
4175    }
4176
4177    /// Get the text byte range end of this segment in the original text.
4178    pub fn text_end(&self) -> usize {
4179        self.text.segments.0[self.index].text.end
4180    }
4181
4182    /// Get the text bytes range of the `glyph_range` in this segment's [`text`].
4183    ///
4184    /// [`text`]: Self::text
4185    pub fn text_glyph_range(&self, glyph_range: impl ops::RangeBounds<usize>) -> ops::Range<usize> {
4186        let included_start = match glyph_range.start_bound() {
4187            ops::Bound::Included(i) => Some(*i),
4188            ops::Bound::Excluded(i) => Some(*i + 1),
4189            ops::Bound::Unbounded => None,
4190        };
4191        let excluded_end = match glyph_range.end_bound() {
4192            ops::Bound::Included(i) => Some(*i - 1),
4193            ops::Bound::Excluded(i) => Some(*i),
4194            ops::Bound::Unbounded => None,
4195        };
4196
4197        let glyph_range_start = self.glyphs_range().start();
4198        let glyph_to_char = |g| self.text.clusters[glyph_range_start + g] as usize;
4199
4200        match (included_start, excluded_end) {
4201            (None, None) => IndexRange(0, self.text_range().len()),
4202            (None, Some(end)) => IndexRange(0, glyph_to_char(end)),
4203            (Some(start), None) => IndexRange(glyph_to_char(start), self.text_range().len()),
4204            (Some(start), Some(end)) => IndexRange(glyph_to_char(start), glyph_to_char(end)),
4205        }
4206        .iter()
4207    }
4208
4209    /// Select the string represented by this segment.
4210    ///
4211    /// The `full_text` must be equal to the original text that was used to generate the parent [`ShapedText`].
4212    pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
4213        let r = self.text_range();
4214        let start = r.start.min(full_text.len());
4215        let end = r.end.min(full_text.len());
4216        &full_text[start..end]
4217    }
4218
4219    /// Gets the insert index in the segment text that is nearest to `x`.
4220    pub fn nearest_char_index(&self, x: Px, full_text: &str) -> usize {
4221        let txt_range = self.text_range();
4222        let is_rtl = self.direction().is_rtl();
4223        let x = x.0 as f32;
4224
4225        let seg_clusters = self.clusters();
4226
4227        for (font, clusters) in self.cluster_glyphs_with_x_advance() {
4228            for (cluster, glyphs, advance) in clusters {
4229                let found = x < glyphs[0].point.x || glyphs[0].point.x + advance > x;
4230                if !found {
4231                    continue;
4232                }
4233                let cluster_i = seg_clusters.iter().position(|&c| c == cluster).unwrap();
4234
4235                let char_a = txt_range.start + cluster as usize;
4236                let char_b = if is_rtl {
4237                    if cluster_i == 0 {
4238                        txt_range.end
4239                    } else {
4240                        txt_range.start + seg_clusters[cluster_i - 1] as usize
4241                    }
4242                } else {
4243                    let next_cluster = cluster_i + glyphs.len();
4244                    if next_cluster == seg_clusters.len() {
4245                        txt_range.end
4246                    } else {
4247                        txt_range.start + seg_clusters[next_cluster] as usize
4248                    }
4249                };
4250
4251                if char_b - char_a > 1 && glyphs.len() == 1 {
4252                    // maybe ligature
4253
4254                    let text = &full_text[char_a..char_b];
4255
4256                    let mut lig_parts = smallvec::SmallVec::<[u16; 6]>::new_const();
4257                    for (i, _) in unicode_segmentation::UnicodeSegmentation::grapheme_indices(text, true) {
4258                        lig_parts.push(i as u16);
4259                    }
4260
4261                    if lig_parts.len() > 1 {
4262                        // is ligature
4263
4264                        let x = x - glyphs[0].point.x;
4265
4266                        let mut split = true;
4267                        for (i, font_caret) in font.ligature_caret_offsets(glyphs[0].index).enumerate() {
4268                            if i == lig_parts.len() {
4269                                break;
4270                            }
4271                            split = false;
4272
4273                            if font_caret > x {
4274                                // found font defined caret
4275                                return char_a + lig_parts[i] as usize;
4276                            }
4277                        }
4278                        if split {
4279                            // no font caret, ligature glyph is split in equal parts
4280                            let lig_part = advance / lig_parts.len() as f32;
4281                            let mut lig_x = lig_part;
4282                            if is_rtl {
4283                                for c in lig_parts.into_iter().rev() {
4284                                    if lig_x > x {
4285                                        // fond
4286                                        return char_a + c as usize;
4287                                    }
4288                                    lig_x += lig_part;
4289                                }
4290                            } else {
4291                                for c in lig_parts {
4292                                    if lig_x > x {
4293                                        return char_a + c as usize;
4294                                    }
4295                                    lig_x += lig_part;
4296                                }
4297                            }
4298                        }
4299                    }
4300                }
4301                // not ligature
4302
4303                let middle_x = glyphs[0].point.x + advance / 2.0;
4304
4305                return if is_rtl {
4306                    if x <= middle_x { char_b } else { char_a }
4307                } else if x <= middle_x {
4308                    char_a
4309                } else {
4310                    char_b
4311                };
4312            }
4313        }
4314
4315        let mut start = is_rtl;
4316        if matches!(self.kind(), TextSegmentKind::LineBreak) {
4317            start = !start;
4318        }
4319        if start { txt_range.start } else { txt_range.end }
4320    }
4321
4322    /// Gets the segment index in the line.
4323    pub fn index(&self) -> usize {
4324        self.index - self.text.lines.segs(self.line_index).start()
4325    }
4326}
4327
4328const WORD_CACHE_MAX_LEN: usize = 32;
4329const WORD_CACHE_MAX_ENTRIES: usize = 10_000;
4330
4331#[derive(Hash, PartialEq, Eq)]
4332pub(super) struct WordCacheKey<S> {
4333    string: S,
4334    ctx_key: WordContextKey,
4335}
4336#[derive(Hash)]
4337struct WordCacheKeyRef<'a, S> {
4338    string: &'a S,
4339    ctx_key: &'a WordContextKey,
4340}
4341
4342#[derive(Hash, PartialEq, Eq, Clone)]
4343pub(super) struct WordContextKey {
4344    lang: unic_langid::subtags::Language,
4345    script: Option<unic_langid::subtags::Script>,
4346    direction: LayoutDirection,
4347    features: Box<[usize]>,
4348}
4349impl WordContextKey {
4350    pub fn new(lang: &Lang, direction: LayoutDirection, font_features: &RFontFeatures) -> Self {
4351        let is_64 = mem::size_of::<usize>() == mem::size_of::<u64>();
4352
4353        let mut features = vec![];
4354
4355        if !font_features.is_empty() {
4356            features.reserve(font_features.len() * if is_64 { 3 } else { 4 });
4357            for feature in font_features {
4358                if is_64 {
4359                    let mut h = feature.tag.0 as u64;
4360                    h |= (feature.value as u64) << 32;
4361                    features.push(h as usize);
4362                } else {
4363                    features.push(feature.tag.0 as usize);
4364                    features.push(feature.value as usize);
4365                }
4366
4367                features.push(feature.start as usize);
4368                features.push(feature.end as usize);
4369            }
4370        }
4371
4372        WordContextKey {
4373            lang: lang.language,
4374            script: lang.script,
4375            direction,
4376            features: features.into_boxed_slice(),
4377        }
4378    }
4379
4380    pub fn harfbuzz_lang(&self) -> Option<rustybuzz::Language> {
4381        self.lang.as_str().parse().ok()
4382    }
4383
4384    pub fn harfbuzz_script(&self) -> Option<rustybuzz::Script> {
4385        let t: u32 = self.script?.into();
4386        let t = t.to_le_bytes(); // Script is a TinyStr4 that uses LE
4387        rustybuzz::Script::from_iso15924_tag(ttf_parser::Tag::from_bytes(&[t[0], t[1], t[2], t[3]]))
4388    }
4389
4390    pub fn harfbuzz_direction(&self) -> rustybuzz::Direction {
4391        into_harf_direction(self.direction)
4392    }
4393}
4394
4395#[derive(Debug, Clone, Default)]
4396pub(super) struct ShapedSegmentData {
4397    glyphs: Vec<ShapedGlyph>,
4398    x_advance: f32,
4399    y_advance: f32,
4400}
4401#[derive(Debug, Clone, Copy)]
4402struct ShapedGlyph {
4403    /// glyph index
4404    index: u32,
4405    /// char index
4406    cluster: u32,
4407    point: (f32, f32),
4408}
4409
4410impl Font {
4411    fn buffer_segment(&self, segment: &str, key: &WordContextKey) -> rustybuzz::UnicodeBuffer {
4412        let mut buffer = rustybuzz::UnicodeBuffer::new();
4413        buffer.set_direction(key.harfbuzz_direction());
4414        buffer.set_cluster_level(rustybuzz::BufferClusterLevel::MonotoneCharacters);
4415
4416        if let Some(lang) = key.harfbuzz_lang() {
4417            buffer.set_language(lang);
4418        }
4419        if let Some(script) = key.harfbuzz_script() {
4420            buffer.set_script(script);
4421        }
4422
4423        buffer.push_str(segment);
4424        buffer
4425    }
4426
4427    fn shape_segment_no_cache(&self, seg: &str, key: &WordContextKey, features: &[rustybuzz::Feature]) -> ShapedSegmentData {
4428        let buffer = if let Some(font) = self.harfbuzz() {
4429            let buffer = self.buffer_segment(seg, key);
4430            rustybuzz::shape(&font, features, buffer)
4431        } else {
4432            return ShapedSegmentData {
4433                glyphs: vec![],
4434                x_advance: 0.0,
4435                y_advance: 0.0,
4436            };
4437        };
4438
4439        let size_scale = self.metrics().size_scale;
4440        let to_layout = |p: i32| p as f32 * size_scale;
4441
4442        let mut w_x_advance = 0.0;
4443        let mut w_y_advance = 0.0;
4444
4445        let glyphs: Vec<_> = buffer
4446            .glyph_infos()
4447            .iter()
4448            .zip(buffer.glyph_positions())
4449            .map(|(i, p)| {
4450                let x_offset = to_layout(p.x_offset);
4451                let y_offset = -to_layout(p.y_offset);
4452                let x_advance = to_layout(p.x_advance);
4453                let y_advance = to_layout(p.y_advance);
4454
4455                let point = (w_x_advance + x_offset, w_y_advance + y_offset);
4456                w_x_advance += x_advance;
4457                w_y_advance += y_advance;
4458
4459                ShapedGlyph {
4460                    index: i.glyph_id,
4461                    cluster: i.cluster,
4462                    point,
4463                }
4464            })
4465            .collect();
4466
4467        ShapedSegmentData {
4468            glyphs,
4469            x_advance: w_x_advance,
4470            y_advance: w_y_advance,
4471        }
4472    }
4473
4474    fn shape_segment<R>(
4475        &self,
4476        seg: &str,
4477        word_ctx_key: &WordContextKey,
4478        features: &[rustybuzz::Feature],
4479        out: impl FnOnce(&ShapedSegmentData) -> R,
4480    ) -> R {
4481        if !(1..=WORD_CACHE_MAX_LEN).contains(&seg.len()) || self.face().is_empty() {
4482            let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4483            out(&seg)
4484        } else if let Some(small) = Self::to_small_word(seg) {
4485            // try cached
4486            let cache = self.0.small_word_cache.read();
4487
4488            let hash = cache.hasher().hash_one(WordCacheKeyRef {
4489                string: &small,
4490                ctx_key: word_ctx_key,
4491            });
4492
4493            if let Some((_, seg)) = cache
4494                .raw_entry()
4495                .from_hash(hash, |e| e.string == small && &e.ctx_key == word_ctx_key)
4496            {
4497                return out(seg);
4498            }
4499            drop(cache);
4500
4501            // shape and cache, can end-up shaping the same word here, but that is better then write locking
4502            let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4503            let key = WordCacheKey {
4504                string: small,
4505                ctx_key: word_ctx_key.clone(),
4506            };
4507            let r = out(&seg);
4508            let mut cache = self.0.small_word_cache.write();
4509            if cache.len() > WORD_CACHE_MAX_ENTRIES {
4510                cache.clear();
4511            }
4512            cache.insert(key, seg);
4513            r
4514        } else {
4515            // try cached
4516            let cache = self.0.word_cache.read();
4517
4518            let hash = cache.hasher().hash_one(WordCacheKeyRef {
4519                string: &seg,
4520                ctx_key: word_ctx_key,
4521            });
4522
4523            if let Some((_, seg)) = cache
4524                .raw_entry()
4525                .from_hash(hash, |e| e.string.as_str() == seg && &e.ctx_key == word_ctx_key)
4526            {
4527                return out(seg);
4528            }
4529            drop(cache);
4530
4531            // shape and cache, can end-up shaping the same word here, but that is better then write locking
4532            let string = seg.to_owned();
4533            let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4534            let key = WordCacheKey {
4535                string,
4536                ctx_key: word_ctx_key.clone(),
4537            };
4538            let r = out(&seg);
4539            let mut cache = self.0.word_cache.write();
4540            if cache.len() > WORD_CACHE_MAX_ENTRIES {
4541                cache.clear();
4542            }
4543            cache.insert(key, seg);
4544            r
4545        }
4546    }
4547
4548    /// Glyph index for the space `' '` character.
4549    pub fn space_index(&self) -> GlyphIndex {
4550        self.shape_space().0
4551    }
4552
4553    /// Returns the horizontal advance of the space `' '` character.
4554    pub fn space_x_advance(&self) -> Px {
4555        self.shape_space().1
4556    }
4557
4558    fn shape_space(&self) -> (GlyphIndex, Px) {
4559        let mut id = 0;
4560        let mut adv = 0.0;
4561        self.shape_segment(
4562            " ",
4563            &WordContextKey {
4564                lang: unic_langid::subtags::Language::from_bytes(b"und").unwrap(),
4565                script: None,
4566                direction: LayoutDirection::LTR,
4567                features: Box::new([]),
4568            },
4569            &[],
4570            |r| {
4571                id = r.glyphs.last().map(|g| g.index).unwrap_or(0);
4572                adv = r.x_advance;
4573            },
4574        );
4575        (id, Px(adv as _))
4576    }
4577
4578    /// Calculates a [`ShapedText`].
4579    pub fn shape_text(self: &Font, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4580        ShapedTextBuilder::shape_text(std::slice::from_ref(self), text, config)
4581    }
4582
4583    /// Sends the sized vector path for a glyph to `sink`.
4584    ///
4585    /// Returns the glyph bounds if a full outline was sent to the sink.
4586    pub fn outline(&self, glyph_id: GlyphIndex, sink: &mut impl OutlineSink) -> Option<PxRect> {
4587        struct AdapterSink<'a, S> {
4588            sink: &'a mut S,
4589            scale: f32,
4590        }
4591        impl<S: OutlineSink> ttf_parser::OutlineBuilder for AdapterSink<'_, S> {
4592            fn move_to(&mut self, x: f32, y: f32) {
4593                self.sink.move_to(euclid::point2(x, y) * self.scale)
4594            }
4595
4596            fn line_to(&mut self, x: f32, y: f32) {
4597                self.sink.line_to(euclid::point2(x, y) * self.scale)
4598            }
4599
4600            fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
4601                let ctrl = euclid::point2(x1, y1) * self.scale;
4602                let to = euclid::point2(x, y) * self.scale;
4603                self.sink.quadratic_curve_to(ctrl, to)
4604            }
4605
4606            fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
4607                let l_from = euclid::point2(x1, y1) * self.scale;
4608                let l_to = euclid::point2(x2, y2) * self.scale;
4609                let to = euclid::point2(x, y) * self.scale;
4610                self.sink.cubic_curve_to((l_from, l_to), to)
4611            }
4612
4613            fn close(&mut self) {
4614                self.sink.close()
4615            }
4616        }
4617
4618        let scale = self.metrics().size_scale;
4619
4620        let f = self.harfbuzz()?;
4621        let r = f.outline_glyph(ttf_parser::GlyphId(glyph_id as _), &mut AdapterSink { sink, scale })?;
4622        Some(
4623            PxRect::new(
4624                PxPoint::new(Px(r.x_min as _), Px(r.y_min as _)),
4625                PxSize::new(Px(r.width() as _), Px(r.height() as _)),
4626            ) * Factor2d::uniform(scale),
4627        )
4628    }
4629
4630    /// Ray cast an horizontal line across the glyph and returns the entry and exit hits.
4631    ///
4632    /// The `line_y_range` are two vertical offsets relative to the baseline, the offsets define
4633    /// the start and inclusive end of the horizontal line, that is, `(underline, underline + thickness)`, note
4634    /// that positions under the baseline are negative so a 2px underline set 1px under the baseline becomes `(-1.0, -3.0)`.
4635    ///
4636    /// Returns `Ok(Some(x_enter, x_exit))` where the two values are x-advances, returns `None` if there is no hit.
4637    /// The first x-advance is from the left typographic border to the first hit on the outline,
4638    /// the second x-advance is from the first across the outline to the exit hit.
4639    pub fn h_line_hits(&self, glyph_id: GlyphIndex, line_y_range: (f32, f32)) -> Option<(f32, f32)> {
4640        // Algorithm:
4641        //
4642        // - Ignore curves, everything is direct line.
4643        // - If a line-y crosses `line_y_range` register the min-x and max-x from the two points.
4644        // - Same if a line is inside `line_y_range`.
4645        struct InterceptsSink {
4646            start: Option<euclid::Point2D<f32, Px>>,
4647            current: euclid::Point2D<f32, Px>,
4648            under: (bool, bool),
4649
4650            line_y_range: (f32, f32),
4651            hit: Option<(f32, f32)>,
4652        }
4653        impl OutlineSink for InterceptsSink {
4654            fn move_to(&mut self, to: euclid::Point2D<f32, Px>) {
4655                self.start = Some(to);
4656                self.current = to;
4657                self.under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4658            }
4659
4660            fn line_to(&mut self, to: euclid::Point2D<f32, Px>) {
4661                let under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4662
4663                if self.under != under || under == (true, false) {
4664                    // crossed one or two y-range boundaries or both points are inside
4665                    self.under = under;
4666
4667                    let (x0, x1) = if self.current.x < to.x {
4668                        (self.current.x, to.x)
4669                    } else {
4670                        (to.x, self.current.x)
4671                    };
4672                    if let Some((min, max)) = &mut self.hit {
4673                        *min = min.min(x0);
4674                        *max = max.max(x1);
4675                    } else {
4676                        self.hit = Some((x0, x1));
4677                    }
4678                }
4679
4680                self.current = to;
4681                self.under = under;
4682            }
4683
4684            fn quadratic_curve_to(&mut self, _: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>) {
4685                self.line_to(to);
4686            }
4687
4688            fn cubic_curve_to(&mut self, _: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>) {
4689                self.line_to(to);
4690            }
4691
4692            fn close(&mut self) {
4693                if let Some(s) = self.start.take()
4694                    && s != self.current
4695                {
4696                    self.line_to(s);
4697                }
4698            }
4699        }
4700        let mut sink = InterceptsSink {
4701            start: None,
4702            current: euclid::point2(0.0, 0.0),
4703            under: (false, false),
4704
4705            line_y_range,
4706            hit: None,
4707        };
4708        self.outline(glyph_id, &mut sink)?;
4709
4710        sink.hit.map(|(a, b)| (a, b - a))
4711    }
4712}
4713
4714/// Receives Bézier path rendering commands from [`Font::outline`].
4715///
4716/// The points are relative to the baseline, negative values under, positive over.
4717pub trait OutlineSink {
4718    /// Moves the pen to a point.
4719    fn move_to(&mut self, to: euclid::Point2D<f32, Px>);
4720    /// Draws a line to a point.
4721    fn line_to(&mut self, to: euclid::Point2D<f32, Px>);
4722    /// Draws a quadratic Bézier curve to a point.
4723    fn quadratic_curve_to(&mut self, ctrl: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>);
4724    /// Draws a cubic Bézier curve to a point.
4725    ///
4726    /// The `ctrl` is a line (from, to).
4727    fn cubic_curve_to(&mut self, ctrl: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>);
4728    /// Closes the path, returning to the first point in it.
4729    fn close(&mut self);
4730}
4731
4732impl FontList {
4733    /// Calculates a [`ShapedText`] using the [best](FontList::best) font in this list and the other fonts as fallback.
4734    pub fn shape_text(&self, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4735        ShapedTextBuilder::shape_text(self, text, config)
4736    }
4737}
4738
4739/// Like [`std::ops::Range<usize>`], but implements [`Copy`].
4740#[derive(Clone, Copy)]
4741struct IndexRange(pub usize, pub usize);
4742impl fmt::Debug for IndexRange {
4743    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4744        write!(f, "{}..{}", self.0, self.1)
4745    }
4746}
4747impl IntoIterator for IndexRange {
4748    type Item = usize;
4749
4750    type IntoIter = std::ops::Range<usize>;
4751
4752    fn into_iter(self) -> Self::IntoIter {
4753        self.iter()
4754    }
4755}
4756impl From<IndexRange> for std::ops::Range<usize> {
4757    fn from(c: IndexRange) -> Self {
4758        c.iter()
4759    }
4760}
4761impl From<std::ops::Range<usize>> for IndexRange {
4762    fn from(r: std::ops::Range<usize>) -> Self {
4763        IndexRange(r.start, r.end)
4764    }
4765}
4766impl IndexRange {
4767    pub fn from_bounds(bounds: impl ops::RangeBounds<usize>) -> Self {
4768        // start..end
4769        let start = match bounds.start_bound() {
4770            ops::Bound::Included(&i) => i,
4771            ops::Bound::Excluded(&i) => i + 1,
4772            ops::Bound::Unbounded => 0,
4773        };
4774        let end = match bounds.end_bound() {
4775            ops::Bound::Included(&i) => i + 1,
4776            ops::Bound::Excluded(&i) => i,
4777            ops::Bound::Unbounded => 0,
4778        };
4779        Self(start, end)
4780    }
4781
4782    /// Into `Range<usize>`.
4783    pub fn iter(self) -> std::ops::Range<usize> {
4784        self.0..self.1
4785    }
4786
4787    /// `self.0`
4788    pub fn start(self) -> usize {
4789        self.0
4790    }
4791
4792    /// `self.1`
4793    pub fn end(self) -> usize {
4794        self.1
4795    }
4796
4797    /// `self.end - self.start`
4798    pub fn len(self) -> usize {
4799        self.end() - self.start()
4800    }
4801}
4802impl std::ops::RangeBounds<usize> for IndexRange {
4803    fn start_bound(&self) -> std::ops::Bound<&usize> {
4804        std::ops::Bound::Included(&self.0)
4805    }
4806
4807    fn end_bound(&self) -> std::ops::Bound<&usize> {
4808        std::ops::Bound::Excluded(&self.1)
4809    }
4810}
4811
4812/// `f32` comparison, panics for `NaN`.
4813pub fn f32_cmp(a: &f32, b: &f32) -> std::cmp::Ordering {
4814    a.partial_cmp(b).unwrap()
4815}
4816
4817fn into_harf_direction(d: LayoutDirection) -> rustybuzz::Direction {
4818    match d {
4819        LayoutDirection::LTR => rustybuzz::Direction::LeftToRight,
4820        LayoutDirection::RTL => rustybuzz::Direction::RightToLeft,
4821    }
4822}
4823
4824#[cfg(test)]
4825mod tests {
4826    use crate::{
4827        FONTS, Font, FontName, FontStretch, FontStyle, FontWeight, ParagraphBreak, SegmentedText, TextReshapingArgs, TextShapingArgs,
4828        WordContextKey,
4829    };
4830    use zng_app::APP;
4831    use zng_ext_l10n::lang;
4832    use zng_layout::{
4833        context::LayoutDirection,
4834        unit::{Px, PxConstraints2d},
4835    };
4836
4837    fn test_font() -> Font {
4838        let mut app = APP.minimal().run_headless(false);
4839        let font = app
4840            .run_test(async {
4841                FONTS
4842                    .normal(&FontName::sans_serif(), &lang!(und))
4843                    .wait_rsp()
4844                    .await
4845                    .unwrap()
4846                    .sized(Px(20), vec![])
4847            })
4848            .unwrap();
4849        drop(app);
4850        font
4851    }
4852
4853    #[test]
4854    fn set_line_spacing() {
4855        let text = "0\n1\n2\n3\n4";
4856        test_line_spacing(text, Px(20), Px(0));
4857        test_line_spacing(text, Px(0), Px(20));
4858        test_line_spacing(text, Px(4), Px(6));
4859        test_line_spacing(text, Px(4), Px(4));
4860        test_line_spacing("a line\nanother\nand another", Px(20), Px(0));
4861        test_line_spacing("", Px(20), Px(0));
4862        test_line_spacing("a line", Px(20), Px(0));
4863    }
4864    fn test_line_spacing(text: &'static str, from: Px, to: Px) {
4865        let font = test_font();
4866        let mut config = TextShapingArgs {
4867            line_height: Px(40),
4868            line_spacing: from,
4869            ..Default::default()
4870        };
4871
4872        let text = SegmentedText::new(text, LayoutDirection::LTR);
4873        let mut test = font.shape_text(&text, &config);
4874
4875        config.line_spacing = to;
4876        let expected = font.shape_text(&text, &config);
4877
4878        assert_eq!(from, test.line_spacing());
4879        test.reshape_lines(&TextReshapingArgs {
4880            constraints: PxConstraints2d::new_fill_size(test.align_size()),
4881            inline_constraints: None,
4882            align: test.align(),
4883            overflow_align: test.overflow_align(),
4884            direction: test.direction(),
4885            line_height: test.line_height(),
4886            line_spacing: to,
4887            paragraph_spacing: Px(0),
4888            paragraph_indent: (Px(0), false),
4889            paragraph_break: ParagraphBreak::None,
4890        });
4891        assert_eq!(to, test.line_spacing());
4892
4893        for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4894            assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4895        }
4896
4897        assert_eq!(test.size(), expected.size());
4898    }
4899
4900    #[test]
4901    fn set_line_height() {
4902        let text = "0\n1\n2\n3\n4";
4903        test_line_height(text, Px(20), Px(20));
4904        test_line_height(text, Px(20), Px(10));
4905        test_line_height(text, Px(10), Px(20));
4906        test_line_height("a line\nanother\nand another", Px(20), Px(10));
4907        test_line_height("", Px(20), Px(10));
4908        test_line_height("a line", Px(20), Px(10));
4909    }
4910    fn test_line_height(text: &'static str, from: Px, to: Px) {
4911        let font = test_font();
4912        let mut config = TextShapingArgs {
4913            line_height: from,
4914            line_spacing: Px(20),
4915            ..Default::default()
4916        };
4917
4918        let text = SegmentedText::new(text, LayoutDirection::LTR);
4919        let mut test = font.shape_text(&text, &config);
4920
4921        config.line_height = to;
4922        let expected = font.shape_text(&text, &config);
4923
4924        assert_eq!(from, test.line_height());
4925        test.reshape_lines(&TextReshapingArgs {
4926            constraints: PxConstraints2d::new_fill_size(test.align_size()),
4927            inline_constraints: None,
4928            align: test.align(),
4929            overflow_align: test.overflow_align(),
4930            direction: test.direction(),
4931            line_height: to,
4932            line_spacing: test.line_spacing(),
4933            paragraph_spacing: Px(0),
4934            paragraph_indent: (Px(0), false),
4935            paragraph_break: ParagraphBreak::None,
4936        });
4937        assert_eq!(to, test.line_height());
4938
4939        for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4940            assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4941        }
4942
4943        assert_eq!(test.size(), expected.size());
4944    }
4945
4946    #[test]
4947    fn font_fallback_issue() {
4948        let mut app = APP.minimal().run_headless(false);
4949        app.run_test(async {
4950            let font = FONTS
4951                .list(
4952                    &[FontName::new("Consolas"), FontName::monospace()],
4953                    FontStyle::Normal,
4954                    FontWeight::NORMAL,
4955                    FontStretch::NORMAL,
4956                    &lang!(und),
4957                )
4958                .wait_rsp()
4959                .await
4960                .sized(Px(20), vec![]);
4961
4962            let config = TextShapingArgs::default();
4963
4964            let txt_seg = SegmentedText::new("النص ثنائي الاتجاه (بالإنجليزية:Bi", LayoutDirection::RTL);
4965            let txt_shape = font.shape_text(&txt_seg, &config);
4966
4967            let _ok = (txt_seg, txt_shape);
4968        })
4969        .unwrap()
4970    }
4971
4972    #[test]
4973    fn cluster_is_byte() {
4974        let font = test_font();
4975
4976        let data = font.shape_segment_no_cache("£a", &WordContextKey::new(&lang!("en-US"), LayoutDirection::LTR, &vec![]), &[]);
4977
4978        for ((i, _), g) in "£a".char_indices().zip(&data.glyphs) {
4979            assert_eq!(i as u32, g.cluster);
4980        }
4981    }
4982}