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    pub fn highlight_rects(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = PxRect> + '_ {
1943        let start_origin = self.caret_origin(range.start, full_txt).x;
1944        let end_origin = self.caret_origin(range.end, full_txt).x;
1945
1946        MergingRectIter::new(
1947            self.lines()
1948                .skip(range.start.line)
1949                .take(range.end.line + 1 - range.start.line)
1950                .flat_map(|l| l.segs())
1951                .skip_while(move |s| s.text_end() <= range.start.index)
1952                .take_while(move |s| s.text_start() < range.end.index)
1953                .map(move |s| {
1954                    let mut r = s.rect();
1955
1956                    if s.text_start() <= range.start.index {
1957                        // first segment in selection
1958
1959                        match s.direction() {
1960                            LayoutDirection::LTR => {
1961                                r.size.width = r.max_x() - start_origin;
1962                                r.origin.x = start_origin;
1963                            }
1964                            LayoutDirection::RTL => {
1965                                r.size.width = start_origin - r.origin.x;
1966                            }
1967                        }
1968                    }
1969                    if s.text_end() > range.end.index {
1970                        // last segment in selection
1971
1972                        match s.direction() {
1973                            LayoutDirection::LTR => {
1974                                r.size.width = end_origin - r.origin.x;
1975                            }
1976                            LayoutDirection::RTL => {
1977                                r.size.width = r.max_x() - end_origin;
1978                                r.origin.x = end_origin;
1979                            }
1980                        }
1981                    }
1982
1983                    r
1984                }),
1985        )
1986    }
1987
1988    /// Clip under/overline to a text `clip_range` area, if `clip_out` only lines outside the range are visible.
1989    pub fn clip_lines(
1990        &self,
1991        clip_range: ops::Range<CaretIndex>,
1992        clip_out: bool,
1993        txt: &str,
1994        lines: impl Iterator<Item = (PxPoint, Px)>,
1995    ) -> Vec<(PxPoint, Px)> {
1996        let clips: Vec<_> = self.highlight_rects(clip_range, txt).collect();
1997
1998        let mut out_lines = vec![];
1999
2000        if clip_out {
2001            let mut exclude_buf = vec![];
2002            for (origin, width) in lines {
2003                let line_max = origin.x + width;
2004
2005                for clip in clips.iter() {
2006                    if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2007                        // line contains
2008                        if origin.x < clip.max_x() && line_max > clip.origin.x {
2009                            // intersects
2010                            exclude_buf.push((clip.origin.x, clip.max_x()));
2011                        }
2012                    }
2013                }
2014
2015                if !exclude_buf.is_empty() {
2016                    // clips don't overlap, enforce LTR
2017                    exclude_buf.sort_by_key(|(s, _)| *s);
2018
2019                    if origin.x < exclude_buf[0].0 {
2020                        // bit before the first clip
2021                        out_lines.push((origin, exclude_buf[0].0 - origin.x));
2022                    }
2023                    let mut blank_start = exclude_buf[0].1;
2024                    for (clip_start, clip_end) in exclude_buf.drain(..).skip(1) {
2025                        if clip_start > blank_start {
2026                            // space between clips
2027                            if line_max > clip_start {
2028                                // bit in-between two clips
2029                                out_lines.push((PxPoint::new(blank_start, origin.y), line_max.min(clip_start) - blank_start));
2030                            }
2031                            blank_start = clip_end;
2032                        }
2033                    }
2034                    if line_max > blank_start {
2035                        // bit after the last clip
2036                        out_lines.push((PxPoint::new(blank_start, origin.y), line_max - blank_start));
2037                    }
2038                } else {
2039                    // not clipped
2040                    out_lines.push((origin, width));
2041                }
2042            }
2043        } else {
2044            let mut include_buf = vec![];
2045            for (origin, width) in lines {
2046                let line_max = origin.x + width;
2047
2048                for clip in clips.iter() {
2049                    if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2050                        // line contains
2051                        if origin.x < clip.max_x() && line_max > clip.origin.x {
2052                            // intersects
2053                            include_buf.push((clip.origin.x, clip.max_x()));
2054                        }
2055                    }
2056                }
2057
2058                if !include_buf.is_empty() {
2059                    include_buf.sort_by_key(|(s, _)| *s);
2060
2061                    for (clip_start, clip_end) in include_buf.drain(..) {
2062                        let start = clip_start.max(origin.x);
2063                        let end = clip_end.min(line_max);
2064
2065                        out_lines.push((PxPoint::new(start, origin.y), end - start));
2066                    }
2067
2068                    include_buf.clear();
2069                }
2070            }
2071        }
2072
2073        out_lines
2074    }
2075}
2076
2077struct ImageGlyphsIter<'a, G>
2078where
2079    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2080{
2081    glyphs: G,
2082    glyphs_i: u32,
2083    images: &'a [(u32, GlyphImage)],
2084    maybe_img: Option<(&'a Font, &'a [GlyphInstance])>,
2085}
2086impl<'a, G> Iterator for ImageGlyphsIter<'a, G>
2087where
2088    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2089{
2090    type Item = (&'a Font, ShapedImageGlyphs<'a>);
2091
2092    fn next(&mut self) -> Option<Self::Item> {
2093        loop {
2094            if let Some((font, glyphs)) = &mut self.maybe_img {
2095                // new glyph sequence or single emoji(maybe img)
2096
2097                // advance images to the next in or after glyph sequence
2098                while self.images.first().map(|(i, _)| *i < self.glyphs_i).unwrap_or(false) {
2099                    self.images = &self.images[1..];
2100                }
2101
2102                if let Some((i, img)) = self.images.first() {
2103                    // if there is still images
2104                    if *i == self.glyphs_i {
2105                        // if the next glyph is replaced by image
2106                        self.glyphs_i += 1;
2107                        let mut size = img.0.with(|i| i.size()).cast::<f32>();
2108                        let scale = font.size().0 as f32 / size.width.max(size.height);
2109                        size *= scale;
2110                        let r = (
2111                            *font,
2112                            ShapedImageGlyphs::Image {
2113                                rect: euclid::Rect::new(glyphs[0].point - euclid::vec2(0.0, size.height), size),
2114                                base_glyph: glyphs[0].index,
2115                                img: &img.0,
2116                            },
2117                        );
2118                        *glyphs = &glyphs[1..];
2119                        if glyphs.is_empty() {
2120                            self.maybe_img = None;
2121                        }
2122                        return Some(r);
2123                    } else {
2124                        // if the next glyph is not replaced by image, yield slice to end or next image
2125                        let normal = &glyphs[..glyphs.len().min(*i as _)];
2126                        self.glyphs_i += normal.len() as u32;
2127
2128                        *glyphs = &glyphs[normal.len()..];
2129                        let r = (*font, ShapedImageGlyphs::Normal(normal));
2130
2131                        if glyphs.is_empty() {
2132                            self.maybe_img = None;
2133                        }
2134                        return Some(r);
2135                    }
2136                } else {
2137                    // if there are no more images
2138                    let r = (*font, ShapedImageGlyphs::Normal(glyphs));
2139                    self.maybe_img = None;
2140                    return Some(r);
2141                }
2142            } else if let Some(seq) = self.glyphs.next() {
2143                // all sequences can contain images
2144                self.maybe_img = Some(seq);
2145            } else {
2146                // no more glyphs to yield
2147                return None;
2148            }
2149        }
2150    }
2151}
2152
2153struct ColoredGlyphsIter<'a, G>
2154where
2155    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2156{
2157    glyphs: G,
2158    maybe_colored: Option<(&'a Font, &'a [GlyphInstance])>,
2159}
2160impl<'a, G> Iterator for ColoredGlyphsIter<'a, G>
2161where
2162    G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2163{
2164    type Item = (&'a Font, ShapedColoredGlyphs<'a>);
2165
2166    fn next(&mut self) -> Option<Self::Item> {
2167        loop {
2168            if let Some((font, glyphs)) = self.maybe_colored {
2169                // maybe-colored iter
2170
2171                let color_glyphs = font.face().color_glyphs();
2172
2173                for (i, g) in glyphs.iter().enumerate() {
2174                    if let Some(c_glyphs) = color_glyphs.glyph(g.index) {
2175                        // colored yield
2176
2177                        let next_start = i + 1;
2178                        if next_start < glyphs.len() {
2179                            // continue maybe-colored iter
2180                            self.maybe_colored = Some((font, &glyphs[next_start..]));
2181                        } else {
2182                            // continue normal iter
2183                            self.maybe_colored = None;
2184                        }
2185
2186                        return Some((
2187                            font,
2188                            ShapedColoredGlyphs::Colored {
2189                                point: g.point,
2190                                base_glyph: g.index,
2191                                glyphs: c_glyphs,
2192                            },
2193                        ));
2194                    }
2195                }
2196                // enter normal iter
2197                self.maybe_colored = None;
2198
2199                // last normal in maybe-colored yield
2200                debug_assert!(!glyphs.is_empty());
2201                return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2202            } else if let Some((font, glyphs)) = self.glyphs.next() {
2203                // normal iter
2204
2205                let color_glyphs = font.face().color_glyphs();
2206                if color_glyphs.is_empty() {
2207                    return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2208                } else {
2209                    // enter maybe-colored iter
2210                    self.maybe_colored = Some((font, glyphs));
2211                    continue;
2212                }
2213            } else {
2214                return None;
2215            }
2216        }
2217    }
2218}
2219
2220/// Info about a shaped text overflow in constraint.
2221///
2222/// Can be computed using [`ShapedText::overflow_info`].
2223#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2224#[non_exhaustive]
2225pub struct TextOverflowInfo {
2226    /// First overflow line.
2227    ///
2228    /// All segments in this line and next lines are fully overflown. The previous line may
2229    /// be partially overflown, the lines before that are fully visible.
2230    ///
2231    /// Is the [`ShapedText::lines_len`] if the last line is fully visible.
2232    pub line: usize,
2233
2234    /// First overflow character in the text.
2235    ///
2236    /// Note that if overflow is not wrapping (single text line) the char may not cover all visible
2237    /// glyphs in the line if it is bidirectional.
2238    pub text_char: usize,
2239
2240    /// Glyphs not overflown in the last not overflown line.
2241    ///
2242    /// If the line is not bidirectional this will be a single range covering the not overflow glyphs,
2243    /// if it is bidi multiple ranges are possible due to bidi reordering.
2244    pub included_glyphs: smallvec::SmallVec<[ops::Range<usize>; 1]>,
2245
2246    /// Placement of the suffix (ellipses or custom).
2247    ///
2248    /// The suffix must be of the width given to [`ShapedText::overflow_info`] and the same line height
2249    /// as the text.
2250    pub suffix_origin: euclid::Point2D<f32, Px>,
2251}
2252
2253trait FontListRef {
2254    /// Shape segment, try fallback fonts if a glyph in the segment is not resolved.
2255    fn shape_segment<R>(
2256        &self,
2257        seg: &str,
2258        word_ctx_key: &WordContextKey,
2259        features: &[rustybuzz::Feature],
2260        out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2261    ) -> R;
2262}
2263impl FontListRef for [Font] {
2264    fn shape_segment<R>(
2265        &self,
2266        seg: &str,
2267        word_ctx_key: &WordContextKey,
2268        features: &[rustybuzz::Feature],
2269        out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2270    ) -> R {
2271        let mut out = Some(out);
2272        let last = self.len() - 1;
2273        for font in &self[..last] {
2274            let r = font.shape_segment(seg, word_ctx_key, features, |seg| {
2275                if seg.glyphs.iter().all(|g| g.index != 0) {
2276                    Some(out.take().unwrap()(seg, font))
2277                } else {
2278                    None
2279                }
2280            });
2281            if let Some(r) = r {
2282                return r;
2283            }
2284        }
2285        self[last].shape_segment(seg, word_ctx_key, features, move |seg| out.unwrap()(seg, &self[last]))
2286    }
2287}
2288
2289struct ShapedTextBuilder {
2290    out: ShapedText,
2291
2292    line_height: f32,
2293    line_spacing: f32,
2294    paragraph_spacing: f32,
2295    word_spacing: f32,
2296    letter_spacing: f32,
2297    paragraph_indent: (f32, bool),
2298    max_width: f32,
2299    break_words: bool,
2300    hyphen_glyphs: (ShapedSegmentData, Font),
2301    tab_x_advance: f32,
2302    tab_index: u32,
2303    hyphens: Hyphens,
2304    lang: Lang,
2305
2306    origin: euclid::Point2D<f32, ()>,
2307    allow_first_wrap: bool,
2308    first_line_max: f32,
2309    mid_clear_min: f32,
2310    max_line_x: f32,
2311    text_seg_end: usize,
2312    line_has_ltr: bool,
2313    line_has_rtl: bool,
2314}
2315impl ShapedTextBuilder {
2316    fn actual_max_width(&self) -> f32 {
2317        if self.out.lines.0.is_empty() && !self.out.first_wrapped {
2318            self.first_line_max.min(self.max_width)
2319        } else {
2320            self.max_width
2321        }
2322    }
2323
2324    fn shape_text(fonts: &[Font], text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
2325        let mut t = Self {
2326            out: ShapedText {
2327                glyphs: Default::default(),
2328                clusters: Default::default(),
2329                segments: Default::default(),
2330                lines: Default::default(),
2331                fonts: Default::default(),
2332                line_height: Default::default(),
2333                line_spacing: Default::default(),
2334                orig_line_height: Default::default(),
2335                orig_line_spacing: Default::default(),
2336                orig_first_line: Default::default(),
2337                orig_last_line: Default::default(),
2338                paragraph_break: Default::default(),
2339                paragraph_spacing: Default::default(),
2340                baseline: Default::default(),
2341                overline: Default::default(),
2342                strikethrough: Default::default(),
2343                underline: Default::default(),
2344                underline_descent: Default::default(),
2345                mid_offset: 0.0,
2346                align_size: PxSize::zero(),
2347                align: Align::TOP_LEFT,
2348                justify: Justify::Auto,
2349                justified: vec![],
2350                overflow_align: Align::TOP_LEFT,
2351                direction: LayoutDirection::LTR,
2352                first_wrapped: false,
2353                is_inlined: config.inline_constraints.is_some(),
2354                first_line: PxRect::zero(),
2355                mid_clear: Px(0),
2356                mid_size: PxSize::zero(),
2357                last_line: PxRect::zero(),
2358                has_colored_glyphs: false,
2359                images: vec![],
2360            },
2361
2362            line_height: 0.0,
2363            line_spacing: 0.0,
2364            paragraph_spacing: 0.0,
2365            word_spacing: 0.0,
2366            letter_spacing: 0.0,
2367            paragraph_indent: (0.0, false),
2368            max_width: 0.0,
2369            break_words: false,
2370            hyphen_glyphs: (ShapedSegmentData::default(), fonts[0].clone()),
2371            tab_x_advance: 0.0,
2372            tab_index: 0,
2373            hyphens: config.hyphens,
2374            lang: config.lang.clone(),
2375            allow_first_wrap: false,
2376
2377            origin: euclid::point2(0.0, 0.0),
2378            first_line_max: f32::INFINITY,
2379            mid_clear_min: 0.0,
2380            max_line_x: 0.0,
2381            text_seg_end: 0,
2382            line_has_ltr: false,
2383            line_has_rtl: false,
2384        };
2385
2386        let mut word_ctx_key = WordContextKey::new(&config.lang, config.direction, &config.font_features);
2387
2388        let metrics = fonts[0].metrics();
2389
2390        t.out.orig_line_height = config.line_height;
2391        t.out.orig_line_spacing = config.line_spacing;
2392        t.out.line_height = config.line_height;
2393        t.out.line_spacing = config.line_spacing;
2394        t.out.paragraph_spacing = config.paragraph_spacing;
2395        t.out.paragraph_break = config.paragraph_break;
2396
2397        t.line_height = config.line_height.0 as f32;
2398        t.line_spacing = config.line_spacing.0 as f32;
2399        t.paragraph_spacing = config.paragraph_spacing.0 as f32;
2400        t.paragraph_indent = (config.paragraph_indent.0.0 as f32, config.paragraph_indent.1);
2401        let baseline = metrics.ascent + metrics.line_gap / 2.0;
2402
2403        t.out.baseline = t.out.line_height - baseline;
2404        t.out.underline = t.out.baseline + metrics.underline_position;
2405        t.out.underline_descent = t.out.baseline + metrics.descent + Px(1);
2406        t.out.strikethrough = t.out.baseline + metrics.ascent / 3.0;
2407        t.out.overline = t.out.baseline + metrics.ascent;
2408
2409        let dft_line_height = metrics.line_height().0 as f32;
2410        let center_height = (t.line_height - dft_line_height) / 2.0;
2411
2412        t.origin = euclid::point2::<_, ()>(0.0, baseline.0 as f32 + center_height);
2413        if !t.paragraph_indent.1 {
2414            // not invert
2415            t.origin.x = t.paragraph_indent.0;
2416        }
2417
2418        t.max_line_x = 0.0;
2419        if let Some(inline) = config.inline_constraints {
2420            t.first_line_max = inline.first_max.0 as f32;
2421            t.mid_clear_min = inline.mid_clear_min.0 as f32;
2422            t.allow_first_wrap = true;
2423        } else {
2424            t.first_line_max = f32::INFINITY;
2425            t.mid_clear_min = 0.0;
2426            t.allow_first_wrap = false;
2427        }
2428
2429        t.letter_spacing = config.letter_spacing.0 as f32;
2430        t.word_spacing = config.word_spacing.0 as f32;
2431        t.tab_x_advance = config.tab_x_advance.0 as f32;
2432        t.tab_index = fonts[0].space_index();
2433
2434        t.max_width = if config.max_width == Px::MAX {
2435            f32::INFINITY
2436        } else {
2437            config.max_width.0 as f32
2438        };
2439
2440        t.break_words = match config.word_break {
2441            WordBreak::Normal => {
2442                lang!("ch").matches(&config.lang, true, false)
2443                    || lang!("jp").matches(&config.lang, true, false)
2444                    || lang!("ko").matches(&config.lang, true, false)
2445            }
2446            WordBreak::BreakAll => true,
2447            WordBreak::KeepAll => false,
2448        };
2449
2450        if !matches!(config.hyphens, Hyphens::None) && t.max_width.is_finite() && config.obscuring_char.is_none() {
2451            // "hyphen" can be any char and we need the x-advance for the wrap algorithm.
2452            t.hyphen_glyphs = fonts.shape_segment(config.hyphen_char.as_str(), &word_ctx_key, &config.font_features, |s, f| {
2453                (s.clone(), f.clone())
2454            });
2455        }
2456
2457        if let Some(c) = config.obscuring_char {
2458            t.push_obscured_text(fonts, &config.font_features, &mut word_ctx_key, text, c);
2459        } else {
2460            t.push_text(fonts, &config.font_features, &mut word_ctx_key, text);
2461        }
2462
2463        t.out.glyphs.shrink_to_fit();
2464        t.out.clusters.shrink_to_fit();
2465        t.out.segments.0.shrink_to_fit();
2466        t.out.lines.0.shrink_to_fit();
2467        t.out.fonts.0.shrink_to_fit();
2468        t.out.images.shrink_to_fit();
2469
2470        t.out.debug_assert_ranges();
2471        t.out
2472    }
2473
2474    fn push_obscured_text(
2475        &mut self,
2476        fonts: &[Font],
2477        features: &RFontFeatures,
2478        word_ctx_key: &mut WordContextKey,
2479        text: &SegmentedText,
2480        obscuring_char: char,
2481    ) {
2482        if text.is_empty() {
2483            self.push_last_line(text);
2484            self.push_font(&fonts[0]);
2485            return;
2486        }
2487
2488        let (glyphs, font) = fonts.shape_segment(Txt::from_char(obscuring_char).as_str(), word_ctx_key, features, |s, f| {
2489            (s.clone(), f.clone())
2490        });
2491
2492        for (seg, info) in text.iter() {
2493            let mut seg_glyphs = ShapedSegmentData::default();
2494            for (cluster, _) in seg.char_indices() {
2495                let i = seg_glyphs.glyphs.len();
2496                seg_glyphs.glyphs.extend(glyphs.glyphs.iter().copied());
2497                for g in &mut seg_glyphs.glyphs[i..] {
2498                    g.point.0 += seg_glyphs.x_advance;
2499                    g.cluster = cluster as u32;
2500                }
2501                seg_glyphs.x_advance += glyphs.x_advance;
2502            }
2503            self.push_glyphs(&seg_glyphs, self.letter_spacing);
2504            self.push_text_seg(seg, info);
2505        }
2506
2507        self.push_last_line(text);
2508
2509        self.push_font(&font);
2510    }
2511
2512    fn push_text(&mut self, fonts: &[Font], features: &RFontFeatures, word_ctx_key: &mut WordContextKey, text: &SegmentedText) {
2513        static LIG: [&[u8]; 4] = [b"liga", b"clig", b"dlig", b"hlig"];
2514        let ligature_enabled = fonts[0].face().has_ligatures()
2515            && features.iter().any(|f| {
2516                let tag = f.tag.to_bytes();
2517                LIG.iter().any(|l| *l == tag)
2518            });
2519
2520        if ligature_enabled {
2521            let mut start = 0;
2522            let mut words_start = None;
2523            for (i, info) in text.segs().iter().enumerate() {
2524                if info.kind.is_word() && info.kind != TextSegmentKind::Emoji {
2525                    if words_start.is_none() {
2526                        words_start = Some(i);
2527                    }
2528                } else {
2529                    if let Some(s) = words_start.take() {
2530                        self.push_ligature_words(fonts, features, word_ctx_key, text, s, i);
2531                    }
2532
2533                    let seg = &text.text()[start..info.end];
2534                    self.push_seg(fonts, features, word_ctx_key, text, seg, *info);
2535                }
2536                start = info.end;
2537            }
2538            if let Some(s) = words_start.take() {
2539                self.push_ligature_words(fonts, features, word_ctx_key, text, s, text.segs().len());
2540            }
2541        } else {
2542            for (seg, info) in text.iter() {
2543                self.push_seg(fonts, features, word_ctx_key, text, seg, info);
2544            }
2545        }
2546
2547        self.push_last_line(text);
2548
2549        self.push_font(&fonts[0]);
2550    }
2551    fn push_ligature_words(
2552        &mut self,
2553        fonts: &[Font],
2554        features: &RFontFeatures,
2555        word_ctx_key: &mut WordContextKey,
2556        text: &SegmentedText,
2557        words_start: usize,
2558        words_end: usize,
2559    ) {
2560        let seg_start = if words_start == 0 { 0 } else { text.segs()[words_start - 1].end };
2561        let end_info = text.segs()[words_end - 1];
2562        let seg_end = end_info.end;
2563        let seg = &text.text()[seg_start..seg_end];
2564
2565        if words_end - words_start == 1 {
2566            self.push_seg(fonts, features, word_ctx_key, text, seg, end_info);
2567        } else {
2568            // check if `is_word` sequence is a ligature that covers more than one word.
2569            let handled = fonts[0].shape_segment(seg, word_ctx_key, features, |shaped_seg| {
2570                let mut cluster_start = 0;
2571                let mut cluster_end = None;
2572                for g in shaped_seg.glyphs.iter() {
2573                    if g.index == 0 {
2574                        // top font not used for at least one word in this sequence
2575                        return false;
2576                    }
2577                    if seg[cluster_start as usize..g.cluster as usize].chars().take(2).count() > 1 {
2578                        cluster_end = Some(g.index);
2579                        break;
2580                    }
2581                    cluster_start = g.cluster;
2582                }
2583
2584                if cluster_end.is_none() && seg[cluster_start as usize..].chars().take(2).count() > 1 {
2585                    cluster_end = Some(seg.len() as u32);
2586                }
2587                if let Some(cluster_end) = cluster_end {
2588                    // previous glyph is a ligature, check word boundaries.
2589                    let cluster_start_in_txt = seg_start + cluster_start as usize;
2590                    let cluster_end_in_txt = seg_start + cluster_end as usize;
2591
2592                    let handle = text.segs()[words_start..words_end]
2593                        .iter()
2594                        .any(|info| info.end > cluster_start_in_txt && info.end <= cluster_end_in_txt);
2595
2596                    if handle {
2597                        let max_width = self.actual_max_width();
2598                        if self.origin.x + shaped_seg.x_advance > max_width {
2599                            // need wrap
2600                            if shaped_seg.x_advance > max_width {
2601                                // need segment split
2602                                return false;
2603                            }
2604
2605                            self.push_line_break(true, text);
2606                            self.push_glyphs(shaped_seg, self.letter_spacing);
2607                        }
2608                        self.push_glyphs(shaped_seg, self.letter_spacing);
2609                        let mut seg = seg;
2610                        for info in &text.segs()[words_start..words_end] {
2611                            self.push_text_seg(seg, *info);
2612                            seg = "";
2613                        }
2614
2615                        return true;
2616                    }
2617                }
2618
2619                false
2620            });
2621
2622            if !handled {
2623                let mut seg_start = seg_start;
2624                for info in text.segs()[words_start..words_end].iter() {
2625                    let seg = &text.text()[seg_start..info.end];
2626                    self.push_seg(fonts, features, word_ctx_key, text, seg, *info);
2627                    seg_start = info.end;
2628                }
2629            }
2630        }
2631    }
2632    fn push_seg(
2633        &mut self,
2634        fonts: &[Font],
2635        features: &RFontFeatures,
2636        word_ctx_key: &mut WordContextKey,
2637        text: &SegmentedText,
2638        seg: &str,
2639        info: TextSegment,
2640    ) {
2641        word_ctx_key.direction = info.direction();
2642        if info.kind.is_word() {
2643            let max_width = self.actual_max_width();
2644
2645            fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2646                if self.origin.x + shaped_seg.x_advance > max_width {
2647                    // need wrap
2648                    if shaped_seg.x_advance > max_width {
2649                        // need segment split
2650
2651                        // try to hyphenate
2652                        let hyphenated = self.push_hyphenate(seg, font, shaped_seg, info, text);
2653
2654                        if !hyphenated && self.break_words {
2655                            // break word
2656                            self.push_split_seg(shaped_seg, seg, info, self.letter_spacing, text);
2657                        } else if !hyphenated {
2658                            let current_start = if self.out.lines.0.is_empty() {
2659                                0
2660                            } else {
2661                                self.out.lines.last().end
2662                            };
2663                            if !self.out.segments.0[current_start..].is_empty() {
2664                                self.push_line_break(true, text);
2665                            } else if current_start == 0 && self.allow_first_wrap {
2666                                self.out.first_wrapped = true;
2667                            }
2668                            self.push_glyphs(shaped_seg, self.letter_spacing);
2669                            self.push_text_seg(seg, info);
2670                        }
2671                    } else {
2672                        self.push_line_break(true, text);
2673                        self.push_glyphs(shaped_seg, self.letter_spacing);
2674                        self.push_text_seg(seg, info);
2675                    }
2676                } else {
2677                    // don't need wrap
2678                    self.push_glyphs(shaped_seg, self.letter_spacing);
2679                    self.push_text_seg(seg, info);
2680                }
2681
2682                if matches!(info.kind, TextSegmentKind::Emoji) {
2683                    if !font.face().color_glyphs().is_empty() {
2684                        self.out.has_colored_glyphs = true;
2685                    }
2686                    if (font.face().has_raster_images() || (cfg!(feature = "svg") && font.face().has_svg_images()))
2687                        && let Some(ttf) = font.face().ttf()
2688                    {
2689                        for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2690                            let id = ttf_parser::GlyphId(g.index as _);
2691                            let ppm = font.size().0 as u16;
2692                            let glyphs_i = self.out.glyphs.len() - shaped_seg.glyphs.len() + i;
2693                            if let Some(img) = ttf.glyph_raster_image(id, ppm) {
2694                                self.push_glyph_raster(glyphs_i as _, img);
2695                            } else if cfg!(feature = "svg")
2696                                && let Some(img) = ttf.glyph_svg_image(id)
2697                            {
2698                                self.push_glyph_svg(glyphs_i as _, img);
2699                            }
2700                        }
2701                    }
2702                }
2703
2704                self.push_font(font);
2705            });
2706        } else if info.kind.is_space() {
2707            if matches!(info.kind, TextSegmentKind::Tab) {
2708                let max_width = self.actual_max_width();
2709                for (i, _) in seg.char_indices() {
2710                    if self.origin.x + self.tab_x_advance > max_width {
2711                        // normal wrap, advance overflow
2712                        self.push_line_break(true, text);
2713                    }
2714                    let point = euclid::point2(self.origin.x, self.origin.y);
2715                    self.origin.x += self.tab_x_advance;
2716                    self.out.glyphs.push(GlyphInstance::new(self.tab_index, point));
2717                    self.out.clusters.push(i as u32);
2718                }
2719
2720                self.push_text_seg(seg, info);
2721                self.push_font(&fonts[0]);
2722            } else {
2723                let max_width = self.actual_max_width();
2724                fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2725                    if self.origin.x + shaped_seg.x_advance > max_width {
2726                        // need wrap
2727                        if seg.len() > 2 {
2728                            // split spaces
2729                            self.push_split_seg(shaped_seg, seg, info, self.word_spacing, text);
2730                        } else {
2731                            self.push_line_break(true, text);
2732                            self.push_glyphs(shaped_seg, self.word_spacing);
2733                            self.push_text_seg(seg, info);
2734                        }
2735                    } else {
2736                        self.push_glyphs(shaped_seg, self.word_spacing);
2737                        self.push_text_seg(seg, info);
2738                    }
2739
2740                    self.push_font(font);
2741                });
2742            }
2743        } else if info.kind.is_line_break() {
2744            self.push_text_seg(seg, info);
2745            self.push_line_break(false, text);
2746        } else {
2747            self.push_text_seg(seg, info)
2748        }
2749    }
2750
2751    fn push_glyph_raster(&mut self, glyphs_i: u32, img: ttf_parser::RasterGlyphImage) {
2752        use ttf_parser::RasterImageFormat;
2753        let size = PxSize::new(Px(img.width as _), Px(img.height as _));
2754        let bgra_fmt = ImageDataFormat::Bgra8 {
2755            size,
2756            density: None,
2757            original_color_type: ColorType::BGRA8,
2758        };
2759        let bgra_len = img.width as usize * img.height as usize * 4;
2760        let (data, fmt) = match img.format {
2761            RasterImageFormat::PNG => (img.data.to_vec(), ImageDataFormat::from("png")),
2762            RasterImageFormat::BitmapMono => {
2763                // row aligned 1-bitmap
2764                let mut bgra = Vec::with_capacity(bgra_len);
2765                let bytes_per_row = (img.width as usize).div_ceil(8);
2766                for y in 0..img.height as usize {
2767                    let row_start = y * bytes_per_row;
2768                    for x in 0..img.width as usize {
2769                        let byte_index = row_start + x / 8;
2770                        let bit_index = 7 - (x % 8);
2771                        let bit = (img.data[byte_index] >> bit_index) & 1;
2772                        let color = if bit == 1 { [0, 0, 0, 255] } else { [255, 255, 255, 255] };
2773                        bgra.extend_from_slice(&color);
2774                    }
2775                }
2776                (bgra, bgra_fmt)
2777            }
2778            RasterImageFormat::BitmapMonoPacked => {
2779                // packed 1-bitmap
2780                let mut bgra = Vec::with_capacity(bgra_len);
2781                for &c8 in img.data {
2782                    for bit in 0..8 {
2783                        let color = if (c8 >> (7 - bit)) & 1 == 1 {
2784                            [0, 0, 0, 255]
2785                        } else {
2786                            [255, 255, 255, 255]
2787                        };
2788                        bgra.extend_from_slice(&color);
2789                        if bgra.len() == bgra_len {
2790                            break;
2791                        }
2792                    }
2793                }
2794                (bgra, bgra_fmt)
2795            }
2796            RasterImageFormat::BitmapGray2 => {
2797                // row aligned 2-bitmap
2798                let mut bgra = Vec::with_capacity(bgra_len);
2799                let bytes_per_row = (img.width as usize).div_ceil(4);
2800                for y in 0..img.height as usize {
2801                    let row_start = y * bytes_per_row;
2802                    for x in 0..img.width as usize {
2803                        let byte_index = row_start + x / 4;
2804                        let shift = (3 - (x % 4)) * 2;
2805                        let gray = (img.data[byte_index] >> shift) & 0b11;
2806                        let color = match gray {
2807                            0b00 => [0, 0, 0, 255],       // Black
2808                            0b01 => [85, 85, 85, 255],    // Dark gray
2809                            0b10 => [170, 170, 170, 255], // Light gray
2810                            0b11 => [255, 255, 255, 255], // White
2811                            _ => unreachable!(),
2812                        };
2813                        bgra.extend_from_slice(&color);
2814                    }
2815                }
2816                (bgra, bgra_fmt)
2817            }
2818            RasterImageFormat::BitmapGray2Packed => {
2819                // packed 2-bitmap
2820                let mut bgra = Vec::with_capacity(bgra_len);
2821                for &c4 in img.data {
2822                    for i in 0..4 {
2823                        let gray = (c4 >> (7 - i * 2)) & 0b11;
2824                        let color = match gray {
2825                            0b00 => [0, 0, 0, 255],       // Black
2826                            0b01 => [85, 85, 85, 255],    // Dark gray
2827                            0b10 => [170, 170, 170, 255], // Light gray
2828                            0b11 => [255, 255, 255, 255], // White
2829                            _ => unreachable!(),
2830                        };
2831                        bgra.extend_from_slice(&color);
2832                        if bgra.len() == bgra_len {
2833                            break;
2834                        }
2835                    }
2836                }
2837                (bgra, bgra_fmt)
2838            }
2839            RasterImageFormat::BitmapGray4 => {
2840                // row aligned 4-bitmap
2841                let mut bgra = Vec::with_capacity(bgra_len);
2842                let bytes_per_row = (img.width as usize).div_ceil(2);
2843                for y in 0..img.height as usize {
2844                    let row_start = y * bytes_per_row;
2845                    for x in 0..img.width as usize {
2846                        let byte_index = row_start + x / 2;
2847                        let shift = if x % 2 == 0 { 4 } else { 0 };
2848                        let gray = (img.data[byte_index] >> shift) & 0b1111;
2849                        let g = gray * 17;
2850                        bgra.extend_from_slice(&[g, g, g, 255]);
2851                    }
2852                }
2853                (bgra, bgra_fmt)
2854            }
2855            RasterImageFormat::BitmapGray4Packed => {
2856                let mut bgra = Vec::with_capacity(bgra_len);
2857                for &c2 in img.data {
2858                    for i in 0..2 {
2859                        let gray = (c2 >> (7 - i * 4)) & 0b1111;
2860                        let g = gray * 17;
2861                        bgra.extend_from_slice(&[g, g, g, 255]);
2862                        if bgra.len() == bgra_len {
2863                            break;
2864                        }
2865                    }
2866                }
2867                (bgra, bgra_fmt)
2868            }
2869            RasterImageFormat::BitmapGray8 => {
2870                let mut bgra = Vec::with_capacity(bgra_len);
2871                for &c in img.data {
2872                    bgra.extend_from_slice(&[c, c, c, 255]);
2873                }
2874                (bgra, bgra_fmt)
2875            }
2876            RasterImageFormat::BitmapPremulBgra32 => {
2877                let mut bgra = img.data.to_vec();
2878                for c in bgra.chunks_exact_mut(4) {
2879                    let (b, g, r, a) = (c[0], c[1], c[2], c[3]);
2880                    let unp = if a == 255 {
2881                        [b, g, r]
2882                    } else {
2883                        [
2884                            (b as u32 * 255 / a as u32) as u8,
2885                            (g as u32 * 255 / a as u32) as u8,
2886                            (r as u32 * 255 / a as u32) as u8,
2887                        ]
2888                    };
2889                    c.copy_from_slice(&unp);
2890                }
2891                (bgra, bgra_fmt)
2892            }
2893        };
2894        self.push_glyph_img(glyphs_i, ImageSource::from((data, fmt)));
2895    }
2896
2897    fn push_glyph_svg(&mut self, glyphs_i: u32, img: ttf_parser::svg::SvgDocument) {
2898        self.push_glyph_img(glyphs_i, ImageSource::from((img.data, ImageDataFormat::from("svg"))));
2899    }
2900
2901    fn push_glyph_img(&mut self, glyphs_i: u32, source: ImageSource) {
2902        let img = IMAGES.image(source, ImageOptions::cache(), None);
2903
2904        self.out.images.push((glyphs_i, GlyphImage(img)));
2905    }
2906
2907    fn push_last_line(&mut self, text: &SegmentedText) {
2908        let directions = self.finish_current_line_bidi(text);
2909        self.out.lines.0.push(LineRange {
2910            end: self.out.segments.0.len(),
2911            width: self.origin.x,
2912            x_offset: 0.0,
2913            directions,
2914        });
2915
2916        self.out.update_mid_size();
2917        self.out.update_first_last_lines();
2918        self.out.orig_first_line = self.out.first_line.size;
2919        self.out.orig_last_line = self.out.last_line.size;
2920        if self.out.is_inlined && self.out.lines.0.len() > 1 {
2921            self.out.last_line.origin.y += self.out.mid_clear;
2922        }
2923    }
2924
2925    fn push_hyphenate(&mut self, seg: &str, font: &Font, shaped_seg: &ShapedSegmentData, info: TextSegment, text: &SegmentedText) -> bool {
2926        if !matches!(self.hyphens, Hyphens::Auto) {
2927            return false;
2928        }
2929
2930        let split_points = HYPHENATION.hyphenate(&self.lang, seg);
2931        self.push_hyphenate_pt(&split_points, 0, font, shaped_seg, seg, info, text)
2932    }
2933
2934    #[allow(clippy::too_many_arguments)]
2935    fn push_hyphenate_pt(
2936        &mut self,
2937        split_points: &[usize],
2938        split_points_sub: usize,
2939        font: &Font,
2940        shaped_seg: &ShapedSegmentData,
2941        seg: &str,
2942        info: TextSegment,
2943        text: &SegmentedText,
2944    ) -> bool {
2945        if split_points.is_empty() {
2946            return false;
2947        }
2948
2949        // find the split that fits more letters and hyphen
2950        let mut end_glyph = 0;
2951        let mut end_point_i = 0;
2952        let max_width = self.actual_max_width();
2953        for (i, point) in split_points.iter().enumerate() {
2954            let mut point = *point - split_points_sub;
2955            let mut width = 0.0;
2956            let mut c = u32::MAX;
2957            let mut gi = 0;
2958            // find the first glyph in the cluster at the char byte index `point`
2959            for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2960                width = g.point.0;
2961                if g.cluster != c {
2962                    // advanced cluster, advance point
2963                    if point == 0 {
2964                        break;
2965                    }
2966                    c = g.cluster;
2967                    point -= 1;
2968                }
2969                gi = i;
2970            }
2971
2972            if self.origin.x + width + self.hyphen_glyphs.0.x_advance > max_width {
2973                // fragment+hyphen is to large
2974                if end_glyph == 0 {
2975                    // found no candidate, there is no way to avoid overflow, use smallest option
2976                    end_glyph = gi + 1;
2977                    end_point_i = i + 1;
2978                }
2979                break;
2980            } else {
2981                // found candidate fragment
2982                end_glyph = gi + 1;
2983                end_point_i = i + 1;
2984            }
2985        }
2986
2987        // split and push the first half + hyphen
2988        let end_glyph_x = shaped_seg.glyphs[end_glyph].point.0;
2989        let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
2990
2991        if glyphs_a.is_empty() || glyphs_b.is_empty() {
2992            debug_assert!(false, "invalid hyphenation split");
2993            return false;
2994        }
2995        let end_cluster = glyphs_b[0].cluster;
2996        let (seg_a, seg_b) = seg.split_at(end_cluster as usize);
2997
2998        self.push_glyphs(
2999            &ShapedSegmentData {
3000                glyphs: glyphs_a.to_vec(),
3001                x_advance: end_glyph_x,
3002                y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3003            },
3004            self.word_spacing,
3005        );
3006        self.push_font(font);
3007
3008        self.push_glyphs(&self.hyphen_glyphs.0.clone(), 0.0);
3009        self.push_font(&self.hyphen_glyphs.1.clone());
3010
3011        self.push_text_seg(seg_a, info);
3012
3013        self.push_line_break(true, text);
3014
3015        // adjust the second half to a new line
3016        let mut shaped_seg_b = ShapedSegmentData {
3017            glyphs: glyphs_b.to_vec(),
3018            x_advance: shaped_seg.x_advance - end_glyph_x,
3019            y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3020        };
3021        for g in &mut shaped_seg_b.glyphs {
3022            g.point.0 -= end_glyph_x;
3023            g.cluster -= seg_a.len() as u32;
3024        }
3025
3026        if shaped_seg_b.x_advance > self.actual_max_width() {
3027            // second half still does not fit, try to hyphenate again.
3028            if self.push_hyphenate_pt(
3029                &split_points[end_point_i..],
3030                split_points_sub + seg_a.len(),
3031                font,
3032                &shaped_seg_b,
3033                seg_b,
3034                info,
3035                text,
3036            ) {
3037                return true;
3038            }
3039        }
3040
3041        // push second half
3042        self.push_glyphs(&shaped_seg_b, self.word_spacing);
3043        self.push_text_seg(seg_b, info);
3044        true
3045    }
3046
3047    fn push_glyphs(&mut self, shaped_seg: &ShapedSegmentData, spacing: f32) {
3048        self.out.glyphs.extend(shaped_seg.glyphs.iter().map(|gi| {
3049            let r = GlyphInstance::new(gi.index, euclid::point2(gi.point.0 + self.origin.x, gi.point.1 + self.origin.y));
3050            self.origin.x += spacing;
3051            r
3052        }));
3053        self.out.clusters.extend(shaped_seg.glyphs.iter().map(|gi| gi.cluster));
3054
3055        self.origin.x += shaped_seg.x_advance;
3056        self.origin.y += shaped_seg.y_advance;
3057    }
3058
3059    fn push_line_break(&mut self, soft: bool, text: &SegmentedText) {
3060        if self.out.glyphs.is_empty() && self.allow_first_wrap && soft {
3061            self.out.first_wrapped = true;
3062        } else {
3063            let directions = self.finish_current_line_bidi(text);
3064
3065            self.out.lines.0.push(LineRange {
3066                end: self.out.segments.0.len(),
3067                width: self.origin.x,
3068                x_offset: 0.0,
3069                directions,
3070            });
3071
3072            if self.out.lines.0.len() == 1 {
3073                self.out.first_line = PxRect::from_size(PxSize::new(Px(self.origin.x as i32), Px(self.line_height as i32)));
3074
3075                if !self.out.first_wrapped {
3076                    let mid_clear = (self.mid_clear_min - self.line_height).max(0.0).round();
3077                    self.origin.y += mid_clear;
3078                    self.out.mid_clear = Px(mid_clear as i32);
3079                    self.out.mid_offset = mid_clear;
3080                }
3081            }
3082
3083            self.max_line_x = self.origin.x.max(self.max_line_x);
3084
3085            let is_paragraph_break = match self.out.paragraph_break {
3086                ParagraphBreak::None => false,
3087                ParagraphBreak::Line => !soft,
3088            };
3089
3090            self.origin.x = if self.paragraph_indent.1 != is_paragraph_break {
3091                // first_line && !invert_indent || !first_line && invert_indent
3092                self.paragraph_indent.0
3093            } else {
3094                0.0
3095            };
3096
3097            self.origin.y += if is_paragraph_break {
3098                self.paragraph_spacing
3099            } else {
3100                self.line_height + self.line_spacing
3101            };
3102        }
3103    }
3104
3105    #[must_use]
3106    fn finish_current_line_bidi(&mut self, text: &SegmentedText) -> LayoutDirections {
3107        if self.line_has_rtl {
3108            let seg_start = if self.out.lines.0.is_empty() {
3109                0
3110            } else {
3111                self.out.lines.last().end
3112            };
3113
3114            if self.line_has_ltr {
3115                // mixed direction
3116
3117                let line_segs = seg_start..self.out.segments.0.len();
3118
3119                // compute visual order and offset segments.
3120                let mut x = 0.0;
3121                for i in text.reorder_line_to_ltr(line_segs) {
3122                    let g_range = self.out.segments.glyphs(i);
3123                    if g_range.iter().is_empty() {
3124                        continue;
3125                    }
3126
3127                    let glyphs = &mut self.out.glyphs[g_range.iter()];
3128                    let offset = x - self.out.segments.0[i].x;
3129                    self.out.segments.0[i].x = x;
3130                    for g in glyphs {
3131                        g.point.x += offset;
3132                    }
3133                    x += self.out.segments.0[i].advance;
3134                }
3135            } else {
3136                // entire line RTL
3137                let line_width = self.origin.x;
3138
3139                let mut x = line_width;
3140                for i in seg_start..self.out.segments.0.len() {
3141                    x -= self.out.segments.0[i].advance;
3142
3143                    let g_range = self.out.segments.glyphs(i);
3144
3145                    let glyphs = &mut self.out.glyphs[g_range.iter()];
3146                    let offset = x - self.out.segments.0[i].x;
3147                    self.out.segments.0[i].x = x;
3148                    for g in glyphs {
3149                        g.point.x += offset;
3150                    }
3151                }
3152            }
3153        }
3154
3155        let mut d = LayoutDirections::empty();
3156        d.set(LayoutDirections::LTR, self.line_has_ltr);
3157        d.set(LayoutDirections::RTL, self.line_has_rtl);
3158
3159        self.line_has_ltr = false;
3160        self.line_has_rtl = false;
3161
3162        d
3163    }
3164
3165    pub fn push_text_seg(&mut self, seg: &str, info: TextSegment) {
3166        let g_len = if let Some(l) = self.out.segments.0.last() {
3167            self.out.glyphs.len() - l.end
3168        } else {
3169            self.out.glyphs.len()
3170        };
3171        if g_len > 0 {
3172            self.line_has_ltr |= info.level.is_ltr();
3173            self.line_has_rtl |= info.level.is_rtl();
3174        }
3175
3176        self.text_seg_end += seg.len();
3177
3178        let is_first_of_line =
3179            (!self.out.lines.0.is_empty() && self.out.lines.last().end == self.out.segments.0.len()) || self.out.segments.0.is_empty();
3180        let x = if is_first_of_line {
3181            0.0
3182        } else {
3183            // not first segment of line
3184            self.out.segments.0.last().map(|s| s.x + s.advance).unwrap_or(0.0)
3185        };
3186        self.out.segments.0.push(GlyphSegment {
3187            text: TextSegment {
3188                end: self.text_seg_end,
3189                ..info
3190            },
3191            end: self.out.glyphs.len(),
3192            x,
3193            advance: self.origin.x - x,
3194        });
3195    }
3196
3197    pub fn push_split_seg(&mut self, shaped_seg: &ShapedSegmentData, seg: &str, info: TextSegment, spacing: f32, text: &SegmentedText) {
3198        let mut end_glyph = 0;
3199        let mut end_glyph_x = 0.0;
3200        let max_width = self.actual_max_width();
3201        for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3202            if self.origin.x + g.point.0 > max_width {
3203                break;
3204            }
3205            end_glyph = i;
3206            end_glyph_x = g.point.0;
3207        }
3208
3209        let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3210
3211        if glyphs_a.is_empty() || glyphs_b.is_empty() {
3212            // failed split
3213            self.push_line_break(true, text);
3214            self.push_glyphs(shaped_seg, spacing);
3215            self.push_text_seg(seg, info);
3216        } else {
3217            let (seg_a, seg_b) = seg.split_at(glyphs_b[0].cluster as usize);
3218
3219            let shaped_seg_a = ShapedSegmentData {
3220                glyphs: glyphs_a.to_vec(),
3221                x_advance: end_glyph_x,
3222                y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3223            };
3224            self.push_glyphs(&shaped_seg_a, spacing);
3225            self.push_text_seg(seg_a, info);
3226            self.push_line_break(true, text);
3227
3228            let mut shaped_seg_b = ShapedSegmentData {
3229                glyphs: glyphs_b.to_vec(),
3230                x_advance: shaped_seg.x_advance - end_glyph_x,
3231                y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3232            };
3233            for g in &mut shaped_seg_b.glyphs {
3234                g.point.0 -= shaped_seg_a.x_advance;
3235                g.cluster -= seg_a.len() as u32;
3236            }
3237
3238            if shaped_seg_b.x_advance <= max_width {
3239                self.push_glyphs(&shaped_seg_b, spacing);
3240                self.push_text_seg(seg_b, info);
3241            } else {
3242                self.push_split_seg(&shaped_seg_b, seg_b, info, spacing, text);
3243            }
3244        }
3245    }
3246
3247    fn push_font(&mut self, font: &Font) {
3248        if let Some(last) = self.out.fonts.0.last_mut() {
3249            if &last.font == font {
3250                last.end = self.out.glyphs.len();
3251                return;
3252            } else if last.end == self.out.glyphs.len() {
3253                return;
3254            }
3255        }
3256        self.out.fonts.0.push(FontRange {
3257            font: font.clone(),
3258            end: self.out.glyphs.len(),
3259        })
3260    }
3261}
3262
3263bitflags! {
3264    /// Identifies what direction segments a [`ShapedLine`] has.
3265    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3266    #[serde(transparent)]
3267    pub struct LayoutDirections: u8 {
3268        /// Line has left-to-right segments.
3269        const LTR = 1;
3270        /// Line has right-to-left segments.
3271        const RTL = 2;
3272        /// Line as both left-to-right and right-to-left segments.
3273        ///
3274        /// When this is the case the line segments positions may be re-ordered.
3275        const BIDI = Self::LTR.bits() | Self::RTL.bits();
3276    }
3277}
3278
3279/// Represents a line selection of a [`ShapedText`].
3280#[derive(Clone, Copy)]
3281pub struct ShapedLine<'a> {
3282    text: &'a ShapedText,
3283    // range of segments of this line.
3284    seg_range: IndexRange,
3285    index: usize,
3286    width: Px,
3287}
3288impl fmt::Debug for ShapedLine<'_> {
3289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3290        f.debug_struct("ShapedLine")
3291            .field("seg_range", &self.seg_range)
3292            .field("index", &self.index)
3293            .field("width", &self.width)
3294            .finish_non_exhaustive()
3295    }
3296}
3297impl<'a> ShapedLine<'a> {
3298    /// Height of the line.
3299    pub fn height(&self) -> Px {
3300        if self.index == 0 {
3301            self.text.first_line.height()
3302        } else if self.index == self.text.lines.0.len() - 1 {
3303            self.text.last_line.height()
3304        } else {
3305            self.text.line_height
3306        }
3307    }
3308
3309    /// Width of the line.
3310    pub fn width(&self) -> Px {
3311        if self.index == 0 {
3312            self.text.first_line.width()
3313        } else if self.index == self.text.lines.0.len() - 1 {
3314            self.text.last_line.width()
3315        } else {
3316            self.width
3317        }
3318    }
3319
3320    /// Bounds of the line.
3321    pub fn rect(&self) -> PxRect {
3322        if self.index == 0 {
3323            return self.text.first_line;
3324        }
3325        if self.index == self.text.lines.0.len() - 1 {
3326            return self.text.last_line;
3327        }
3328
3329        let size = PxSize::new(self.width, self.text.line_height);
3330        let origin = PxPoint::new(
3331            Px(self.text.lines.0[self.index].x_offset as i32),
3332            self.text.line_height * Px((self.index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear,
3333        );
3334        PxRect::new(origin, size)
3335    }
3336
3337    /// Initial size of the line, before any line reshaping.
3338    ///
3339    /// This can be different then the current [`rect`] size if the parent inline changed the size, usually to inject
3340    /// blank spaces to justify the text or to visually insert a bidirectional fragment of another widget.
3341    ///
3342    /// [`rect`]: Self::rect
3343    pub fn original_size(&self) -> PxSize {
3344        if self.index == 0 {
3345            return self.text.orig_first_line;
3346        }
3347        if self.index == self.text.lines.0.len() - 1 {
3348            return self.text.orig_last_line;
3349        }
3350        PxSize::new(self.width, self.text.line_height)
3351    }
3352
3353    /// Gets if this line is the first of a paragraph.
3354    ///
3355    /// Paragraphs are defined by the [`paragraph_break`] value.
3356    ///
3357    /// [`paragraph_break`]: ShapedText::paragraph_break
3358    pub fn is_paragraph_start(&self) -> bool {
3359        match self.text.paragraph_break {
3360            ParagraphBreak::None => self.index == 0,
3361            ParagraphBreak::Line => !self.started_by_wrap(),
3362        }
3363    }
3364
3365    /// Gets if this line is the last of a paragraph.
3366    ///
3367    /// Paragraphs are defined by the [`paragraph_break`] value.
3368    ///
3369    /// [`paragraph_break`]: ShapedText::paragraph_break
3370    pub fn is_paragraph_end(&self) -> bool {
3371        match self.text.paragraph_break {
3372            ParagraphBreak::None => self.index == self.text.lines.0.len() - 1,
3373            ParagraphBreak::Line => !self.ended_by_wrap(),
3374        }
3375    }
3376
3377    /// Full overline, start point + width.
3378    pub fn overline(&self) -> (PxPoint, Px) {
3379        self.decoration_line(self.text.overline)
3380    }
3381
3382    /// Full strikethrough line, start point + width.
3383    pub fn strikethrough(&self) -> (PxPoint, Px) {
3384        self.decoration_line(self.text.strikethrough)
3385    }
3386
3387    /// Full underline, not skipping.
3388    ///
3389    /// The *y* is defined by the font metrics.
3390    ///
3391    /// Returns start point + width.
3392    pub fn underline(&self) -> (PxPoint, Px) {
3393        self.decoration_line(self.text.underline)
3394    }
3395
3396    /// Full underline, not skipping.
3397    ///
3398    /// The *y* is the baseline + descent + 1px.
3399    ///
3400    /// Returns start point + width.
3401    pub fn underline_descent(&self) -> (PxPoint, Px) {
3402        self.decoration_line(self.text.underline_descent)
3403    }
3404
3405    /// Underline, skipping spaces.
3406    ///
3407    /// The *y* is defined by the font metrics.
3408    ///
3409    /// Returns and iterator of start point + width for each word.
3410    pub fn underline_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3411        MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline()))
3412    }
3413
3414    /// Underline, skipping spaces.
3415    ///
3416    /// The *y* is the baseline + descent + 1px.
3417    ///
3418    /// Returns and iterator of start point + width for each word.
3419    pub fn underline_descent_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3420        MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline_descent()))
3421    }
3422
3423    /// Underline, skipping glyph descends that intersect the underline.
3424    ///
3425    /// The *y* is defined by the font metrics.
3426    ///
3427    /// Returns an iterator of start point + width for continuous underline.
3428    pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3429        MergingLineIter::new(self.segs().flat_map(move |s| s.underline_skip_glyphs(thickness)))
3430    }
3431
3432    /// Underline, skipping spaces and glyph descends that intersect the underline
3433    ///
3434    /// The *y* is defined by font metrics.
3435    ///
3436    /// Returns an iterator of start point + width for continuous underline.
3437    pub fn underline_skip_glyphs_and_spaces(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3438        MergingLineIter::new(
3439            self.segs()
3440                .filter(|s| s.kind().is_word())
3441                .flat_map(move |s| s.underline_skip_glyphs(thickness)),
3442        )
3443    }
3444
3445    fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3446        let r = self.rect();
3447        let y = r.max_y() - bottom_up_offset;
3448        (PxPoint::new(r.origin.x, y), self.width)
3449    }
3450
3451    fn segments(&self) -> &'a [GlyphSegment] {
3452        &self.text.segments.0[self.seg_range.iter()]
3453    }
3454
3455    /// Glyphs in the line.
3456    ///
3457    /// The glyphs are in text order by segments and in visual order (LTR) within segments, so
3458    /// the RTL text "لما " will have the space glyph first, then "’álif", "miim", "láam".
3459    ///
3460    /// All glyph points are set as offsets to the top-left of the text full text.
3461    pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a {
3462        let r = self.glyphs_range();
3463        self.text.glyphs_range(r)
3464    }
3465
3466    /// Glyphs in the line paired with the *x-advance*.
3467    pub fn glyphs_with_x_advance(&self) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + 'a)> + 'a {
3468        self.segs().flat_map(|s| s.glyphs_with_x_advance())
3469    }
3470
3471    fn glyphs_range(&self) -> IndexRange {
3472        self.text.segments.glyphs_range(self.seg_range)
3473    }
3474
3475    /// Iterate over word and space segments in this line.
3476    pub fn segs(&self) -> impl DoubleEndedIterator<Item = ShapedSegment<'a>> + ExactSizeIterator + use<'a> {
3477        let text = self.text;
3478        let line_index = self.index;
3479        self.seg_range.iter().map(move |i| ShapedSegment {
3480            text,
3481            line_index,
3482            index: i,
3483        })
3484    }
3485
3486    /// Number of segments in this line.
3487    pub fn segs_len(&self) -> usize {
3488        self.seg_range.len()
3489    }
3490
3491    /// Get the segment by index.
3492    ///
3493    /// The first segment of the line is `0`.
3494    pub fn seg(&self, seg_idx: usize) -> Option<ShapedSegment<'_>> {
3495        if self.seg_range.len() > seg_idx {
3496            Some(ShapedSegment {
3497                text: self.text,
3498                line_index: self.index,
3499                index: seg_idx + self.seg_range.start(),
3500            })
3501        } else {
3502            None
3503        }
3504    }
3505
3506    /// Returns `true` if this line was started by the wrap algorithm.
3507    ///
3508    /// If this is `false` then the line is the first or the previous line ends in a [`LineBreak`].
3509    ///
3510    /// [`LineBreak`]: TextSegmentKind::LineBreak
3511    pub fn started_by_wrap(&self) -> bool {
3512        self.index > 0 && {
3513            let prev_line = self.text.lines.segs(self.index - 1);
3514            self.text.segments.0[prev_line.iter()]
3515                .last()
3516                .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3517                .unwrap() // only last line can be empty
3518        }
3519    }
3520
3521    /// Returns `true` if this line was ended by the wrap algorithm.
3522    ///
3523    /// If this is `false` then the line is the last or ends in a [`LineBreak`].
3524    ///
3525    /// [`LineBreak`]: TextSegmentKind::LineBreak
3526    pub fn ended_by_wrap(&self) -> bool {
3527        // not last and not ended in line-break.
3528        self.index < self.text.lines.0.len() - 1
3529            && self
3530                .segments()
3531                .last()
3532                .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3533                .unwrap() // only last line can be empty
3534    }
3535
3536    /// Returns the line or first previous line that is not [`started_by_wrap`].
3537    ///
3538    /// [`started_by_wrap`]: Self::started_by_wrap
3539    pub fn actual_line_start(&self) -> Self {
3540        let mut r = *self;
3541        while r.started_by_wrap() {
3542            r = r.text.line(r.index - 1).unwrap();
3543        }
3544        r
3545    }
3546
3547    /// Returns the line or first next line that is not [`ended_by_wrap`].
3548    ///
3549    /// [`ended_by_wrap`]: Self::ended_by_wrap
3550    pub fn actual_line_end(&self) -> Self {
3551        let mut r = *self;
3552        while r.ended_by_wrap() {
3553            r = r.text.line(r.index + 1).unwrap();
3554        }
3555        r
3556    }
3557
3558    /// Get the text bytes range of this line in the original text.
3559    pub fn text_range(&self) -> ops::Range<usize> {
3560        let start = self.seg_range.start();
3561        let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3562        let end = self.seg_range.end();
3563        let end = if end == 0 { 0 } else { self.text.segments.0[end - 1].text.end };
3564
3565        start..end
3566    }
3567
3568    /// Get the text bytes range of this line in the original text, excluding the line break
3569    /// to keep [`end`] in the same line.
3570    ///
3571    /// [`end`]: ops::Range<usize>::end
3572    pub fn text_caret_range(&self) -> ops::Range<usize> {
3573        let start = self.seg_range.start();
3574        let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3575        let end = self.seg_range.end();
3576        let end = if end == 0 {
3577            0
3578        } else if self.seg_range.start() == end {
3579            start
3580        } else {
3581            let seg = &self.text.segments.0[end - 1];
3582            if !matches!(seg.text.kind, TextSegmentKind::LineBreak) {
3583                seg.text.end
3584            } else {
3585                // start of LineBreak segment
3586                if end == 1 { 0 } else { self.text.segments.0[end - 2].text.end }
3587            }
3588        };
3589
3590        start..end
3591    }
3592
3593    /// Gets the text range of the actual line, joining shaped lines that are started by wrap.
3594    pub fn actual_text_range(&self) -> ops::Range<usize> {
3595        let start = self.actual_line_start().text_range().start;
3596        let end = self.actual_line_end().text_range().end;
3597        start..end
3598    }
3599
3600    /// Gets the text range of the actual line, excluding the line break at the end.
3601    pub fn actual_text_caret_range(&self) -> ops::Range<usize> {
3602        let start = self.actual_line_start().text_range().start;
3603        let end = self.actual_line_end().text_caret_range().end;
3604        start..end
3605    }
3606
3607    /// Select the string represented by this line.
3608    ///
3609    /// The `full_text` must be equal to the original text that was used to generate the parent [`ShapedText`].
3610    pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
3611        let r = self.text_range();
3612
3613        let start = r.start.min(full_text.len());
3614        let end = r.end.min(full_text.len());
3615
3616        &full_text[start..end]
3617    }
3618
3619    /// Gets the segment that contains `x` or is nearest to it.
3620    pub fn nearest_seg(&self, x: Px) -> Option<ShapedSegment<'a>> {
3621        let mut min = None;
3622        let mut min_dist = Px::MAX;
3623        for seg in self.segs() {
3624            let (seg_x, width) = seg.x_width();
3625            if x >= seg_x {
3626                let seg_max_x = seg_x + width;
3627                if x < seg_max_x {
3628                    return Some(seg);
3629                }
3630            }
3631            let dist = (x - seg_x).abs();
3632            if min_dist > dist {
3633                min = Some(seg);
3634                min_dist = dist;
3635            }
3636        }
3637        min
3638    }
3639
3640    /// Gets the line index.
3641    pub fn index(&self) -> usize {
3642        self.index
3643    }
3644
3645    /// Layout directions of segments in this line.
3646    pub fn directions(&self) -> LayoutDirections {
3647        self.text.lines.0[self.index].directions
3648    }
3649}
3650
3651/// Merges lines defined by `(PxPoint, Px)`, assuming the `y` is equal.
3652struct MergingLineIter<I> {
3653    iter: I,
3654    line: Option<(PxPoint, Px)>,
3655}
3656impl<I> MergingLineIter<I> {
3657    pub fn new(iter: I) -> Self {
3658        MergingLineIter { iter, line: None }
3659    }
3660}
3661impl<I: Iterator<Item = (PxPoint, Px)>> Iterator for MergingLineIter<I> {
3662    type Item = I::Item;
3663
3664    fn next(&mut self) -> Option<Self::Item> {
3665        loop {
3666            match self.iter.next() {
3667                Some(line) => {
3668                    if let Some(prev_line) = &mut self.line {
3669                        fn min_x((origin, _width): (PxPoint, Px)) -> Px {
3670                            origin.x
3671                        }
3672                        fn max_x((origin, width): (PxPoint, Px)) -> Px {
3673                            origin.x + width
3674                        }
3675
3676                        if prev_line.0.y == line.0.y && min_x(*prev_line) <= max_x(line) && max_x(*prev_line) >= min_x(line) {
3677                            let x = min_x(*prev_line).min(min_x(line));
3678                            prev_line.1 = max_x(*prev_line).max(max_x(line)) - x;
3679                            prev_line.0.x = x;
3680                        } else {
3681                            let cut = mem::replace(prev_line, line);
3682                            return Some(cut);
3683                        }
3684                    } else {
3685                        self.line = Some(line);
3686                        continue;
3687                    }
3688                }
3689                None => return self.line.take(),
3690            }
3691        }
3692    }
3693}
3694
3695struct MergingRectIter<I> {
3696    iter: I,
3697    rect: Option<PxBox>,
3698}
3699impl<I> MergingRectIter<I> {
3700    pub fn new(iter: I) -> Self {
3701        MergingRectIter { iter, rect: None }
3702    }
3703}
3704impl<I: Iterator<Item = PxRect>> Iterator for MergingRectIter<I> {
3705    type Item = I::Item;
3706
3707    fn next(&mut self) -> Option<Self::Item> {
3708        loop {
3709            match self.iter.next() {
3710                Some(rect) => {
3711                    let rect = rect.to_box2d();
3712                    if let Some(prev_rect) = &mut self.rect {
3713                        if prev_rect.min.y == rect.min.y
3714                            && prev_rect.max.y == rect.max.y
3715                            && prev_rect.min.x <= rect.max.x
3716                            && prev_rect.max.x >= rect.min.x
3717                        {
3718                            prev_rect.min.x = prev_rect.min.x.min(rect.min.x);
3719                            prev_rect.max.x = prev_rect.max.x.max(rect.max.x);
3720                            continue;
3721                        } else {
3722                            let cut = mem::replace(prev_rect, rect);
3723                            return Some(cut.to_rect());
3724                        }
3725                    } else {
3726                        self.rect = Some(rect);
3727                        continue;
3728                    }
3729                }
3730                None => return self.rect.take().map(|r| r.to_rect()),
3731            }
3732        }
3733    }
3734}
3735
3736/// Represents a word or space selection of a [`ShapedText`].
3737#[derive(Clone, Copy)]
3738pub struct ShapedSegment<'a> {
3739    text: &'a ShapedText,
3740    line_index: usize,
3741    index: usize,
3742}
3743impl fmt::Debug for ShapedSegment<'_> {
3744    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3745        f.debug_struct("ShapedSegment")
3746            .field("line_index", &self.line_index)
3747            .field("index", &self.index)
3748            .finish_non_exhaustive()
3749    }
3750}
3751impl<'a> ShapedSegment<'a> {
3752    /// Segment kind.
3753    pub fn kind(&self) -> TextSegmentKind {
3754        self.text.segments.0[self.index].text.kind
3755    }
3756
3757    /// Segment bidi level.
3758    pub fn level(&self) -> BidiLevel {
3759        self.text.segments.0[self.index].text.level
3760    }
3761
3762    /// Layout direction of glyphs in the segment.
3763    pub fn direction(&self) -> LayoutDirection {
3764        self.text.segments.0[self.index].text.direction()
3765    }
3766
3767    /// If the segment contains the last glyph of the line.
3768    pub fn has_last_glyph(&self) -> bool {
3769        let seg_glyphs = self.text.segments.glyphs(self.index);
3770        let s = self.text.lines.segs(self.line_index);
3771        let line_glyphs = self.text.segments.glyphs_range(s);
3772        seg_glyphs.end() == line_glyphs.end()
3773    }
3774
3775    fn glyphs_range(&self) -> IndexRange {
3776        self.text.segments.glyphs(self.index)
3777    }
3778
3779    /// Glyphs in the word or space.
3780    ///
3781    /// The glyphs are in visual order (LTR) within segments, so
3782    /// the RTL text "لما" will yield "’álif", "miim", "láam".
3783    ///
3784    /// All glyph points are set as offsets to the top-left of the text full text.
3785    ///
3786    /// Note that multiple glyphs can map to the same char and multiple chars can map to the same glyph, you can use the [`clusters`]
3787    /// map to find the char for each glyph. Some font ligatures also bridge multiple segments, in this case only the first shaped
3788    /// segment has glyphs the subsequent ones are empty.
3789    ///
3790    /// [`clusters`]: Self::clusters
3791    pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> {
3792        let r = self.glyphs_range();
3793        self.text.glyphs_range(r)
3794    }
3795
3796    /// Gets the specific glyph and font.
3797    pub fn glyph(&self, index: usize) -> Option<(&'a Font, GlyphInstance)> {
3798        let mut r = self.glyphs_range();
3799        r.0 += index;
3800        self.text.glyphs_range(r).next().map(|(f, g)| (f, g[0]))
3801    }
3802
3803    /// Map glyph -> char.
3804    ///
3805    /// Each [`glyphs`] glyph pairs with an entry in this slice that is the char byte index in [`text`]. If
3806    /// a font ligature bridges multiple segments only the first segment will have a non-empty map.
3807    ///
3808    /// [`glyphs`]: Self::glyphs
3809    /// [`text`]: Self::text
3810    pub fn clusters(&self) -> &[u32] {
3811        let r = self.glyphs_range();
3812        self.text.clusters_range(r)
3813    }
3814
3815    /// Count the deduplicated [`clusters`].
3816    ///
3817    /// [`clusters`]: Self::clusters
3818    pub fn clusters_count(&self) -> usize {
3819        let mut c = u32::MAX;
3820        let mut count = 0;
3821        for &i in self.clusters() {
3822            if i != c {
3823                c = i;
3824                count += 1;
3825            }
3826        }
3827        count
3828    }
3829
3830    /// Number of next segments that are empty because their text is included in a ligature
3831    /// glyph or glyphs started in this segment.
3832    pub fn ligature_segs_count(&self) -> usize {
3833        let range = self.glyphs_range();
3834        if range.iter().is_empty() {
3835            0
3836        } else {
3837            self.text.segments.0[self.index + 1..]
3838                .iter()
3839                .filter(|s| s.end == range.end())
3840                .count()
3841        }
3842    }
3843
3844    /// Glyphs in the segment, paired with the *x-advance*.
3845    ///
3846    /// Yields `(Font, [(glyph, advance)])`.
3847    pub fn glyphs_with_x_advance(
3848        &self,
3849    ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + use<'a>)> + use<'a> {
3850        let r = self.glyphs_range();
3851        self.text.seg_glyphs_with_x_advance(self.index, r)
3852    }
3853
3854    /// Glyphs per cluster in the segment, paired with the *x-advance* of the cluster.
3855    ///
3856    /// Yields `(Font, [(cluster, [glyph], advance)])`.
3857    pub fn cluster_glyphs_with_x_advance(
3858        &self,
3859    ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (u32, &'a [GlyphInstance], f32)> + use<'a>)> + use<'a> {
3860        let r = self.glyphs_range();
3861        self.text.seg_cluster_glyphs_with_x_advance(self.index, r)
3862    }
3863
3864    /// Gets the segment x offset and advance.
3865    pub fn x_width(&self) -> (Px, Px) {
3866        let IndexRange(start, end) = self.glyphs_range();
3867
3868        let is_line_break = start == end && matches!(self.kind(), TextSegmentKind::LineBreak);
3869
3870        let start_x = match self.direction() {
3871            LayoutDirection::LTR => {
3872                if is_line_break || start == self.text.glyphs.len() {
3873                    let x = self.text.lines.x_offset(self.line_index);
3874                    let w = self.text.lines.width(self.line_index);
3875                    return (Px((x + w) as i32), Px(0));
3876                }
3877                self.text.glyphs[start].point.x
3878            }
3879            LayoutDirection::RTL => {
3880                if is_line_break || start == self.text.glyphs.len() {
3881                    let x = self.text.lines.x_offset(self.line_index);
3882                    return (Px(x as i32), Px(0));
3883                }
3884
3885                self.text.glyphs[start..end]
3886                    .iter()
3887                    .map(|g| g.point.x)
3888                    .min_by(f32::total_cmp)
3889                    .unwrap_or(0.0)
3890            }
3891        };
3892
3893        (Px(start_x.floor() as i32), Px(self.advance().ceil() as i32))
3894    }
3895
3896    /// Segment exact *width* in pixels.
3897    pub fn advance(&self) -> f32 {
3898        self.text.segments.0[self.index].advance
3899    }
3900
3901    /// Bounds of the word or spaces.
3902    pub fn rect(&self) -> PxRect {
3903        let (x, width) = self.x_width();
3904        let size = PxSize::new(width, self.text.line_height);
3905
3906        let y = if self.line_index == 0 {
3907            self.text.first_line.origin.y
3908        } else if self.line_index == self.text.lines.0.len() - 1 {
3909            self.text.last_line.origin.y
3910        } else {
3911            self.text.line_height * Px((self.line_index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear
3912        };
3913        PxRect::new(PxPoint::new(x, y), size)
3914    }
3915
3916    /// Gets the first char and glyph with advance that overflows `max_width`.
3917    pub fn overflow_char_glyph(&self, max_width_px: f32) -> Option<(usize, usize)> {
3918        if self.advance() > max_width_px {
3919            match self.direction() {
3920                LayoutDirection::LTR => {
3921                    let mut x = 0.0;
3922                    let mut g = 0;
3923                    for (_, c) in self.cluster_glyphs_with_x_advance() {
3924                        for (cluster, glyphs, advance) in c {
3925                            x += advance;
3926                            if x > max_width_px {
3927                                return Some((cluster as usize, g));
3928                            }
3929                            g += glyphs.len();
3930                        }
3931                    }
3932                }
3933                LayoutDirection::RTL => {
3934                    let mut g = 0;
3935                    let mut rev = smallvec::SmallVec::<[_; 10]>::new();
3936                    for (_, c) in self.cluster_glyphs_with_x_advance() {
3937                        for (cluster, glyphs, advance) in c {
3938                            rev.push((cluster, g, advance));
3939                            g += glyphs.len();
3940                        }
3941                    }
3942
3943                    let mut x = 0.0;
3944                    for (c, g, advance) in rev.into_iter().rev() {
3945                        x += advance;
3946                        if x > max_width_px {
3947                            return Some((c as usize, g));
3948                        }
3949                    }
3950                }
3951            }
3952        }
3953        None
3954    }
3955
3956    /// Segment info for widget inline segments.
3957    pub fn inline_info(&self) -> InlineSegmentInfo {
3958        let (x, width) = self.x_width();
3959        InlineSegmentInfo::new(x, width)
3960    }
3961
3962    fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3963        let (x, width) = self.x_width();
3964        let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - bottom_up_offset;
3965        (PxPoint::new(x, y), width)
3966    }
3967
3968    /// Overline spanning the word or spaces, start point + width.
3969    pub fn overline(&self) -> (PxPoint, Px) {
3970        self.decoration_line(self.text.overline)
3971    }
3972
3973    /// Strikethrough spanning the word or spaces, start point + width.
3974    pub fn strikethrough(&self) -> (PxPoint, Px) {
3975        self.decoration_line(self.text.strikethrough)
3976    }
3977
3978    /// Underline spanning the word or spaces, not skipping.
3979    ///
3980    /// The *y* is defined by the font metrics.
3981    ///
3982    /// Returns start point + width.
3983    pub fn underline(&self) -> (PxPoint, Px) {
3984        self.decoration_line(self.text.underline)
3985    }
3986
3987    /// Underline spanning the word or spaces, skipping glyph descends that intercept the line.
3988    ///
3989    /// Returns an iterator of start point + width for underline segments.
3990    pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + use<'a> {
3991        let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - self.text.underline;
3992        let (x, _) = self.x_width();
3993
3994        let line_y = -(self.text.baseline - self.text.underline).0 as f32;
3995        let line_y_range = (line_y, line_y - thickness.0 as f32);
3996
3997        // space around glyph descends, thickness clamped to a minimum of 1px and a maximum of 0.2em (same as Firefox).
3998        let padding = (thickness.0 as f32).clamp(1.0, (self.text.fonts.font(0).size().0 as f32 * 0.2).max(1.0));
3999
4000        // no yield, only sadness
4001        struct UnderlineSkipGlyphs<'a, I, J> {
4002            line_y_range: (f32, f32),
4003            y: Px,
4004            padding: f32,
4005            min_width: Px,
4006
4007            iter: I,
4008            resume: Option<(&'a Font, J)>,
4009            x: f32,
4010            width: f32,
4011        }
4012        impl<I, J> UnderlineSkipGlyphs<'_, I, J> {
4013            fn line(&self) -> Option<(PxPoint, Px)> {
4014                fn f32_to_px(px: f32) -> Px {
4015                    Px(px.round() as i32)
4016                }
4017                let r = (PxPoint::new(f32_to_px(self.x), self.y), f32_to_px(self.width));
4018                if r.1 >= self.min_width { Some(r) } else { None }
4019            }
4020        }
4021        impl<'a, I, J> Iterator for UnderlineSkipGlyphs<'a, I, J>
4022        where
4023            I: Iterator<Item = (&'a Font, J)>,
4024            J: Iterator<Item = (GlyphInstance, f32)>,
4025        {
4026            type Item = (PxPoint, Px);
4027
4028            fn next(&mut self) -> Option<Self::Item> {
4029                loop {
4030                    let continuation = self.resume.take().or_else(|| self.iter.next());
4031                    if let Some((font, mut glyphs_with_adv)) = continuation {
4032                        for (g, a) in &mut glyphs_with_adv {
4033                            if let Some((ex_start, ex_end)) = font.h_line_hits(g.index, self.line_y_range) {
4034                                self.width += ex_start - self.padding;
4035                                let r = self.line();
4036                                self.x += self.width + self.padding + ex_end + self.padding;
4037                                self.width = a - (ex_start + ex_end) - self.padding;
4038
4039                                if r.is_some() {
4040                                    self.resume = Some((font, glyphs_with_adv));
4041                                    return r;
4042                                }
4043                            } else {
4044                                self.width += a;
4045                                // continue
4046                            }
4047                        }
4048                    } else {
4049                        let r = self.line();
4050                        self.width = 0.0;
4051                        return r;
4052                    }
4053                }
4054            }
4055        }
4056        UnderlineSkipGlyphs {
4057            line_y_range,
4058            y,
4059            padding,
4060            min_width: Px((padding / 2.0).max(1.0).ceil() as i32),
4061
4062            iter: self.glyphs_with_x_advance(),
4063            resume: None,
4064            x: x.0 as f32,
4065            width: 0.0,
4066        }
4067    }
4068
4069    /// Underline spanning the word or spaces, not skipping.
4070    ///
4071    /// The *y* is the baseline + descent + 1px.
4072    ///
4073    /// Returns start point + width.
4074    pub fn underline_descent(&self) -> (PxPoint, Px) {
4075        self.decoration_line(self.text.underline_descent)
4076    }
4077
4078    /// Get the text bytes range of this segment in the original text.
4079    pub fn text_range(&self) -> ops::Range<usize> {
4080        self.text_start()..self.text_end()
4081    }
4082
4083    /// Get the text byte range start of this segment in the original text.
4084    pub fn text_start(&self) -> usize {
4085        if self.index == 0 {
4086            0
4087        } else {
4088            self.text.segments.0[self.index - 1].text.end
4089        }
4090    }
4091
4092    /// Get the text byte range end of this segment in the original text.
4093    pub fn text_end(&self) -> usize {
4094        self.text.segments.0[self.index].text.end
4095    }
4096
4097    /// Get the text bytes range of the `glyph_range` in this segment's [`text`].
4098    ///
4099    /// [`text`]: Self::text
4100    pub fn text_glyph_range(&self, glyph_range: impl ops::RangeBounds<usize>) -> ops::Range<usize> {
4101        let included_start = match glyph_range.start_bound() {
4102            ops::Bound::Included(i) => Some(*i),
4103            ops::Bound::Excluded(i) => Some(*i + 1),
4104            ops::Bound::Unbounded => None,
4105        };
4106        let excluded_end = match glyph_range.end_bound() {
4107            ops::Bound::Included(i) => Some(*i - 1),
4108            ops::Bound::Excluded(i) => Some(*i),
4109            ops::Bound::Unbounded => None,
4110        };
4111
4112        let glyph_range_start = self.glyphs_range().start();
4113        let glyph_to_char = |g| self.text.clusters[glyph_range_start + g] as usize;
4114
4115        match (included_start, excluded_end) {
4116            (None, None) => IndexRange(0, self.text_range().len()),
4117            (None, Some(end)) => IndexRange(0, glyph_to_char(end)),
4118            (Some(start), None) => IndexRange(glyph_to_char(start), self.text_range().len()),
4119            (Some(start), Some(end)) => IndexRange(glyph_to_char(start), glyph_to_char(end)),
4120        }
4121        .iter()
4122    }
4123
4124    /// Select the string represented by this segment.
4125    ///
4126    /// The `full_text` must be equal to the original text that was used to generate the parent [`ShapedText`].
4127    pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
4128        let r = self.text_range();
4129        let start = r.start.min(full_text.len());
4130        let end = r.end.min(full_text.len());
4131        &full_text[start..end]
4132    }
4133
4134    /// Gets the insert index in the segment text that is nearest to `x`.
4135    pub fn nearest_char_index(&self, x: Px, full_text: &str) -> usize {
4136        let txt_range = self.text_range();
4137        let is_rtl = self.direction().is_rtl();
4138        let x = x.0 as f32;
4139
4140        let seg_clusters = self.clusters();
4141
4142        for (font, clusters) in self.cluster_glyphs_with_x_advance() {
4143            for (cluster, glyphs, advance) in clusters {
4144                let found = x < glyphs[0].point.x || glyphs[0].point.x + advance > x;
4145                if !found {
4146                    continue;
4147                }
4148                let cluster_i = seg_clusters.iter().position(|&c| c == cluster).unwrap();
4149
4150                let char_a = txt_range.start + cluster as usize;
4151                let char_b = if is_rtl {
4152                    if cluster_i == 0 {
4153                        txt_range.end
4154                    } else {
4155                        txt_range.start + seg_clusters[cluster_i - 1] as usize
4156                    }
4157                } else {
4158                    let next_cluster = cluster_i + glyphs.len();
4159                    if next_cluster == seg_clusters.len() {
4160                        txt_range.end
4161                    } else {
4162                        txt_range.start + seg_clusters[next_cluster] as usize
4163                    }
4164                };
4165
4166                if char_b - char_a > 1 && glyphs.len() == 1 {
4167                    // maybe ligature
4168
4169                    let text = &full_text[char_a..char_b];
4170
4171                    let mut lig_parts = smallvec::SmallVec::<[u16; 6]>::new_const();
4172                    for (i, _) in unicode_segmentation::UnicodeSegmentation::grapheme_indices(text, true) {
4173                        lig_parts.push(i as u16);
4174                    }
4175
4176                    if lig_parts.len() > 1 {
4177                        // is ligature
4178
4179                        let x = x - glyphs[0].point.x;
4180
4181                        let mut split = true;
4182                        for (i, font_caret) in font.ligature_caret_offsets(glyphs[0].index).enumerate() {
4183                            if i == lig_parts.len() {
4184                                break;
4185                            }
4186                            split = false;
4187
4188                            if font_caret > x {
4189                                // found font defined caret
4190                                return char_a + lig_parts[i] as usize;
4191                            }
4192                        }
4193                        if split {
4194                            // no font caret, ligature glyph is split in equal parts
4195                            let lig_part = advance / lig_parts.len() as f32;
4196                            let mut lig_x = lig_part;
4197                            if is_rtl {
4198                                for c in lig_parts.into_iter().rev() {
4199                                    if lig_x > x {
4200                                        // fond
4201                                        return char_a + c as usize;
4202                                    }
4203                                    lig_x += lig_part;
4204                                }
4205                            } else {
4206                                for c in lig_parts {
4207                                    if lig_x > x {
4208                                        return char_a + c as usize;
4209                                    }
4210                                    lig_x += lig_part;
4211                                }
4212                            }
4213                        }
4214                    }
4215                }
4216                // not ligature
4217
4218                let middle_x = glyphs[0].point.x + advance / 2.0;
4219
4220                return if is_rtl {
4221                    if x <= middle_x { char_b } else { char_a }
4222                } else if x <= middle_x {
4223                    char_a
4224                } else {
4225                    char_b
4226                };
4227            }
4228        }
4229
4230        let mut start = is_rtl;
4231        if matches!(self.kind(), TextSegmentKind::LineBreak) {
4232            start = !start;
4233        }
4234        if start { txt_range.start } else { txt_range.end }
4235    }
4236
4237    /// Gets the segment index in the line.
4238    pub fn index(&self) -> usize {
4239        self.index - self.text.lines.segs(self.line_index).start()
4240    }
4241}
4242
4243const WORD_CACHE_MAX_LEN: usize = 32;
4244const WORD_CACHE_MAX_ENTRIES: usize = 10_000;
4245
4246#[derive(Hash, PartialEq, Eq)]
4247pub(super) struct WordCacheKey<S> {
4248    string: S,
4249    ctx_key: WordContextKey,
4250}
4251#[derive(Hash)]
4252struct WordCacheKeyRef<'a, S> {
4253    string: &'a S,
4254    ctx_key: &'a WordContextKey,
4255}
4256
4257#[derive(Hash, PartialEq, Eq, Clone)]
4258pub(super) struct WordContextKey {
4259    lang: unic_langid::subtags::Language,
4260    script: Option<unic_langid::subtags::Script>,
4261    direction: LayoutDirection,
4262    features: Box<[usize]>,
4263}
4264impl WordContextKey {
4265    pub fn new(lang: &Lang, direction: LayoutDirection, font_features: &RFontFeatures) -> Self {
4266        let is_64 = mem::size_of::<usize>() == mem::size_of::<u64>();
4267
4268        let mut features = vec![];
4269
4270        if !font_features.is_empty() {
4271            features.reserve(font_features.len() * if is_64 { 3 } else { 4 });
4272            for feature in font_features {
4273                if is_64 {
4274                    let mut h = feature.tag.0 as u64;
4275                    h |= (feature.value as u64) << 32;
4276                    features.push(h as usize);
4277                } else {
4278                    features.push(feature.tag.0 as usize);
4279                    features.push(feature.value as usize);
4280                }
4281
4282                features.push(feature.start as usize);
4283                features.push(feature.end as usize);
4284            }
4285        }
4286
4287        WordContextKey {
4288            lang: lang.language,
4289            script: lang.script,
4290            direction,
4291            features: features.into_boxed_slice(),
4292        }
4293    }
4294
4295    pub fn harfbuzz_lang(&self) -> Option<rustybuzz::Language> {
4296        self.lang.as_str().parse().ok()
4297    }
4298
4299    pub fn harfbuzz_script(&self) -> Option<rustybuzz::Script> {
4300        let t: u32 = self.script?.into();
4301        let t = t.to_le_bytes(); // Script is a TinyStr4 that uses LE
4302        rustybuzz::Script::from_iso15924_tag(ttf_parser::Tag::from_bytes(&[t[0], t[1], t[2], t[3]]))
4303    }
4304
4305    pub fn harfbuzz_direction(&self) -> rustybuzz::Direction {
4306        into_harf_direction(self.direction)
4307    }
4308}
4309
4310#[derive(Debug, Clone, Default)]
4311pub(super) struct ShapedSegmentData {
4312    glyphs: Vec<ShapedGlyph>,
4313    x_advance: f32,
4314    y_advance: f32,
4315}
4316#[derive(Debug, Clone, Copy)]
4317struct ShapedGlyph {
4318    /// glyph index
4319    index: u32,
4320    /// char index
4321    cluster: u32,
4322    point: (f32, f32),
4323}
4324
4325impl Font {
4326    fn buffer_segment(&self, segment: &str, key: &WordContextKey) -> rustybuzz::UnicodeBuffer {
4327        let mut buffer = rustybuzz::UnicodeBuffer::new();
4328        buffer.set_direction(key.harfbuzz_direction());
4329        buffer.set_cluster_level(rustybuzz::BufferClusterLevel::MonotoneCharacters);
4330
4331        if let Some(lang) = key.harfbuzz_lang() {
4332            buffer.set_language(lang);
4333        }
4334        if let Some(script) = key.harfbuzz_script() {
4335            buffer.set_script(script);
4336        }
4337
4338        buffer.push_str(segment);
4339        buffer
4340    }
4341
4342    fn shape_segment_no_cache(&self, seg: &str, key: &WordContextKey, features: &[rustybuzz::Feature]) -> ShapedSegmentData {
4343        let buffer = if let Some(font) = self.harfbuzz() {
4344            let buffer = self.buffer_segment(seg, key);
4345            rustybuzz::shape(&font, features, buffer)
4346        } else {
4347            return ShapedSegmentData {
4348                glyphs: vec![],
4349                x_advance: 0.0,
4350                y_advance: 0.0,
4351            };
4352        };
4353
4354        let size_scale = self.metrics().size_scale;
4355        let to_layout = |p: i32| p as f32 * size_scale;
4356
4357        let mut w_x_advance = 0.0;
4358        let mut w_y_advance = 0.0;
4359
4360        let glyphs: Vec<_> = buffer
4361            .glyph_infos()
4362            .iter()
4363            .zip(buffer.glyph_positions())
4364            .map(|(i, p)| {
4365                let x_offset = to_layout(p.x_offset);
4366                let y_offset = -to_layout(p.y_offset);
4367                let x_advance = to_layout(p.x_advance);
4368                let y_advance = to_layout(p.y_advance);
4369
4370                let point = (w_x_advance + x_offset, w_y_advance + y_offset);
4371                w_x_advance += x_advance;
4372                w_y_advance += y_advance;
4373
4374                ShapedGlyph {
4375                    index: i.glyph_id,
4376                    cluster: i.cluster,
4377                    point,
4378                }
4379            })
4380            .collect();
4381
4382        ShapedSegmentData {
4383            glyphs,
4384            x_advance: w_x_advance,
4385            y_advance: w_y_advance,
4386        }
4387    }
4388
4389    fn shape_segment<R>(
4390        &self,
4391        seg: &str,
4392        word_ctx_key: &WordContextKey,
4393        features: &[rustybuzz::Feature],
4394        out: impl FnOnce(&ShapedSegmentData) -> R,
4395    ) -> R {
4396        if !(1..=WORD_CACHE_MAX_LEN).contains(&seg.len()) || self.face().is_empty() {
4397            let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4398            out(&seg)
4399        } else if let Some(small) = Self::to_small_word(seg) {
4400            // try cached
4401            let cache = self.0.small_word_cache.read();
4402
4403            let hash = cache.hasher().hash_one(WordCacheKeyRef {
4404                string: &small,
4405                ctx_key: word_ctx_key,
4406            });
4407
4408            if let Some((_, seg)) = cache
4409                .raw_entry()
4410                .from_hash(hash, |e| e.string == small && &e.ctx_key == word_ctx_key)
4411            {
4412                return out(seg);
4413            }
4414            drop(cache);
4415
4416            // shape and cache, can end-up shaping the same word here, but that is better then write locking
4417            let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4418            let key = WordCacheKey {
4419                string: small,
4420                ctx_key: word_ctx_key.clone(),
4421            };
4422            let r = out(&seg);
4423            let mut cache = self.0.small_word_cache.write();
4424            if cache.len() > WORD_CACHE_MAX_ENTRIES {
4425                cache.clear();
4426            }
4427            cache.insert(key, seg);
4428            r
4429        } else {
4430            // try cached
4431            let cache = self.0.word_cache.read();
4432
4433            let hash = cache.hasher().hash_one(WordCacheKeyRef {
4434                string: &seg,
4435                ctx_key: word_ctx_key,
4436            });
4437
4438            if let Some((_, seg)) = cache
4439                .raw_entry()
4440                .from_hash(hash, |e| e.string.as_str() == seg && &e.ctx_key == word_ctx_key)
4441            {
4442                return out(seg);
4443            }
4444            drop(cache);
4445
4446            // shape and cache, can end-up shaping the same word here, but that is better then write locking
4447            let string = seg.to_owned();
4448            let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4449            let key = WordCacheKey {
4450                string,
4451                ctx_key: word_ctx_key.clone(),
4452            };
4453            let r = out(&seg);
4454            let mut cache = self.0.word_cache.write();
4455            if cache.len() > WORD_CACHE_MAX_ENTRIES {
4456                cache.clear();
4457            }
4458            cache.insert(key, seg);
4459            r
4460        }
4461    }
4462
4463    /// Glyph index for the space `' '` character.
4464    pub fn space_index(&self) -> GlyphIndex {
4465        self.shape_space().0
4466    }
4467
4468    /// Returns the horizontal advance of the space `' '` character.
4469    pub fn space_x_advance(&self) -> Px {
4470        self.shape_space().1
4471    }
4472
4473    fn shape_space(&self) -> (GlyphIndex, Px) {
4474        let mut id = 0;
4475        let mut adv = 0.0;
4476        self.shape_segment(
4477            " ",
4478            &WordContextKey {
4479                lang: unic_langid::subtags::Language::from_bytes(b"und").unwrap(),
4480                script: None,
4481                direction: LayoutDirection::LTR,
4482                features: Box::new([]),
4483            },
4484            &[],
4485            |r| {
4486                id = r.glyphs.last().map(|g| g.index).unwrap_or(0);
4487                adv = r.x_advance;
4488            },
4489        );
4490        (id, Px(adv as _))
4491    }
4492
4493    /// Calculates a [`ShapedText`].
4494    pub fn shape_text(self: &Font, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4495        ShapedTextBuilder::shape_text(std::slice::from_ref(self), text, config)
4496    }
4497
4498    /// Sends the sized vector path for a glyph to `sink`.
4499    ///
4500    /// Returns the glyph bounds if a full outline was sent to the sink.
4501    pub fn outline(&self, glyph_id: GlyphIndex, sink: &mut impl OutlineSink) -> Option<PxRect> {
4502        struct AdapterSink<'a, S> {
4503            sink: &'a mut S,
4504            scale: f32,
4505        }
4506        impl<S: OutlineSink> ttf_parser::OutlineBuilder for AdapterSink<'_, S> {
4507            fn move_to(&mut self, x: f32, y: f32) {
4508                self.sink.move_to(euclid::point2(x, y) * self.scale)
4509            }
4510
4511            fn line_to(&mut self, x: f32, y: f32) {
4512                self.sink.line_to(euclid::point2(x, y) * self.scale)
4513            }
4514
4515            fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
4516                let ctrl = euclid::point2(x1, y1) * self.scale;
4517                let to = euclid::point2(x, y) * self.scale;
4518                self.sink.quadratic_curve_to(ctrl, to)
4519            }
4520
4521            fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
4522                let l_from = euclid::point2(x1, y1) * self.scale;
4523                let l_to = euclid::point2(x2, y2) * self.scale;
4524                let to = euclid::point2(x, y) * self.scale;
4525                self.sink.cubic_curve_to((l_from, l_to), to)
4526            }
4527
4528            fn close(&mut self) {
4529                self.sink.close()
4530            }
4531        }
4532
4533        let scale = self.metrics().size_scale;
4534
4535        let f = self.harfbuzz()?;
4536        let r = f.outline_glyph(ttf_parser::GlyphId(glyph_id as _), &mut AdapterSink { sink, scale })?;
4537        Some(
4538            PxRect::new(
4539                PxPoint::new(Px(r.x_min as _), Px(r.y_min as _)),
4540                PxSize::new(Px(r.width() as _), Px(r.height() as _)),
4541            ) * Factor2d::uniform(scale),
4542        )
4543    }
4544
4545    /// Ray cast an horizontal line across the glyph and returns the entry and exit hits.
4546    ///
4547    /// The `line_y_range` are two vertical offsets relative to the baseline, the offsets define
4548    /// the start and inclusive end of the horizontal line, that is, `(underline, underline + thickness)`, note
4549    /// that positions under the baseline are negative so a 2px underline set 1px under the baseline becomes `(-1.0, -3.0)`.
4550    ///
4551    /// Returns `Ok(Some(x_enter, x_exit))` where the two values are x-advances, returns `None` if there is no hit.
4552    /// The first x-advance is from the left typographic border to the first hit on the outline,
4553    /// the second x-advance is from the first across the outline to the exit hit.
4554    pub fn h_line_hits(&self, glyph_id: GlyphIndex, line_y_range: (f32, f32)) -> Option<(f32, f32)> {
4555        // Algorithm:
4556        //
4557        // - Ignore curves, everything is direct line.
4558        // - If a line-y crosses `line_y_range` register the min-x and max-x from the two points.
4559        // - Same if a line is inside `line_y_range`.
4560        struct InterceptsSink {
4561            start: Option<euclid::Point2D<f32, Px>>,
4562            current: euclid::Point2D<f32, Px>,
4563            under: (bool, bool),
4564
4565            line_y_range: (f32, f32),
4566            hit: Option<(f32, f32)>,
4567        }
4568        impl OutlineSink for InterceptsSink {
4569            fn move_to(&mut self, to: euclid::Point2D<f32, Px>) {
4570                self.start = Some(to);
4571                self.current = to;
4572                self.under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4573            }
4574
4575            fn line_to(&mut self, to: euclid::Point2D<f32, Px>) {
4576                let under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4577
4578                if self.under != under || under == (true, false) {
4579                    // crossed one or two y-range boundaries or both points are inside
4580                    self.under = under;
4581
4582                    let (x0, x1) = if self.current.x < to.x {
4583                        (self.current.x, to.x)
4584                    } else {
4585                        (to.x, self.current.x)
4586                    };
4587                    if let Some((min, max)) = &mut self.hit {
4588                        *min = min.min(x0);
4589                        *max = max.max(x1);
4590                    } else {
4591                        self.hit = Some((x0, x1));
4592                    }
4593                }
4594
4595                self.current = to;
4596                self.under = under;
4597            }
4598
4599            fn quadratic_curve_to(&mut self, _: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>) {
4600                self.line_to(to);
4601            }
4602
4603            fn cubic_curve_to(&mut self, _: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>) {
4604                self.line_to(to);
4605            }
4606
4607            fn close(&mut self) {
4608                if let Some(s) = self.start.take()
4609                    && s != self.current
4610                {
4611                    self.line_to(s);
4612                }
4613            }
4614        }
4615        let mut sink = InterceptsSink {
4616            start: None,
4617            current: euclid::point2(0.0, 0.0),
4618            under: (false, false),
4619
4620            line_y_range,
4621            hit: None,
4622        };
4623        self.outline(glyph_id, &mut sink)?;
4624
4625        sink.hit.map(|(a, b)| (a, b - a))
4626    }
4627}
4628
4629/// Receives Bézier path rendering commands from [`Font::outline`].
4630///
4631/// The points are relative to the baseline, negative values under, positive over.
4632pub trait OutlineSink {
4633    /// Moves the pen to a point.
4634    fn move_to(&mut self, to: euclid::Point2D<f32, Px>);
4635    /// Draws a line to a point.
4636    fn line_to(&mut self, to: euclid::Point2D<f32, Px>);
4637    /// Draws a quadratic Bézier curve to a point.
4638    fn quadratic_curve_to(&mut self, ctrl: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>);
4639    /// Draws a cubic Bézier curve to a point.
4640    ///
4641    /// The `ctrl` is a line (from, to).
4642    fn cubic_curve_to(&mut self, ctrl: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>);
4643    /// Closes the path, returning to the first point in it.
4644    fn close(&mut self);
4645}
4646
4647impl FontList {
4648    /// Calculates a [`ShapedText`] using the [best](FontList::best) font in this list and the other fonts as fallback.
4649    pub fn shape_text(&self, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4650        ShapedTextBuilder::shape_text(self, text, config)
4651    }
4652}
4653
4654/// Like [`std::ops::Range<usize>`], but implements [`Copy`].
4655#[derive(Clone, Copy)]
4656struct IndexRange(pub usize, pub usize);
4657impl fmt::Debug for IndexRange {
4658    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4659        write!(f, "{}..{}", self.0, self.1)
4660    }
4661}
4662impl IntoIterator for IndexRange {
4663    type Item = usize;
4664
4665    type IntoIter = std::ops::Range<usize>;
4666
4667    fn into_iter(self) -> Self::IntoIter {
4668        self.iter()
4669    }
4670}
4671impl From<IndexRange> for std::ops::Range<usize> {
4672    fn from(c: IndexRange) -> Self {
4673        c.iter()
4674    }
4675}
4676impl From<std::ops::Range<usize>> for IndexRange {
4677    fn from(r: std::ops::Range<usize>) -> Self {
4678        IndexRange(r.start, r.end)
4679    }
4680}
4681impl IndexRange {
4682    pub fn from_bounds(bounds: impl ops::RangeBounds<usize>) -> Self {
4683        // start..end
4684        let start = match bounds.start_bound() {
4685            ops::Bound::Included(&i) => i,
4686            ops::Bound::Excluded(&i) => i + 1,
4687            ops::Bound::Unbounded => 0,
4688        };
4689        let end = match bounds.end_bound() {
4690            ops::Bound::Included(&i) => i + 1,
4691            ops::Bound::Excluded(&i) => i,
4692            ops::Bound::Unbounded => 0,
4693        };
4694        Self(start, end)
4695    }
4696
4697    /// Into `Range<usize>`.
4698    pub fn iter(self) -> std::ops::Range<usize> {
4699        self.0..self.1
4700    }
4701
4702    /// `self.0`
4703    pub fn start(self) -> usize {
4704        self.0
4705    }
4706
4707    /// `self.1`
4708    pub fn end(self) -> usize {
4709        self.1
4710    }
4711
4712    /// `self.end - self.start`
4713    pub fn len(self) -> usize {
4714        self.end() - self.start()
4715    }
4716}
4717impl std::ops::RangeBounds<usize> for IndexRange {
4718    fn start_bound(&self) -> std::ops::Bound<&usize> {
4719        std::ops::Bound::Included(&self.0)
4720    }
4721
4722    fn end_bound(&self) -> std::ops::Bound<&usize> {
4723        std::ops::Bound::Excluded(&self.1)
4724    }
4725}
4726
4727/// `f32` comparison, panics for `NaN`.
4728pub fn f32_cmp(a: &f32, b: &f32) -> std::cmp::Ordering {
4729    a.partial_cmp(b).unwrap()
4730}
4731
4732fn into_harf_direction(d: LayoutDirection) -> rustybuzz::Direction {
4733    match d {
4734        LayoutDirection::LTR => rustybuzz::Direction::LeftToRight,
4735        LayoutDirection::RTL => rustybuzz::Direction::RightToLeft,
4736    }
4737}
4738
4739#[cfg(test)]
4740mod tests {
4741    use crate::{
4742        FONTS, Font, FontName, FontStretch, FontStyle, FontWeight, ParagraphBreak, SegmentedText, TextReshapingArgs, TextShapingArgs,
4743        WordContextKey,
4744    };
4745    use zng_app::APP;
4746    use zng_ext_l10n::lang;
4747    use zng_layout::{
4748        context::LayoutDirection,
4749        unit::{Px, PxConstraints2d},
4750    };
4751
4752    fn test_font() -> Font {
4753        let mut app = APP.minimal().run_headless(false);
4754        let font = app
4755            .run_test(async {
4756                FONTS
4757                    .normal(&FontName::sans_serif(), &lang!(und))
4758                    .wait_rsp()
4759                    .await
4760                    .unwrap()
4761                    .sized(Px(20), vec![])
4762            })
4763            .unwrap();
4764        drop(app);
4765        font
4766    }
4767
4768    #[test]
4769    fn set_line_spacing() {
4770        let text = "0\n1\n2\n3\n4";
4771        test_line_spacing(text, Px(20), Px(0));
4772        test_line_spacing(text, Px(0), Px(20));
4773        test_line_spacing(text, Px(4), Px(6));
4774        test_line_spacing(text, Px(4), Px(4));
4775        test_line_spacing("a line\nanother\nand another", Px(20), Px(0));
4776        test_line_spacing("", Px(20), Px(0));
4777        test_line_spacing("a line", Px(20), Px(0));
4778    }
4779    fn test_line_spacing(text: &'static str, from: Px, to: Px) {
4780        let font = test_font();
4781        let mut config = TextShapingArgs {
4782            line_height: Px(40),
4783            line_spacing: from,
4784            ..Default::default()
4785        };
4786
4787        let text = SegmentedText::new(text, LayoutDirection::LTR);
4788        let mut test = font.shape_text(&text, &config);
4789
4790        config.line_spacing = to;
4791        let expected = font.shape_text(&text, &config);
4792
4793        assert_eq!(from, test.line_spacing());
4794        test.reshape_lines(&TextReshapingArgs {
4795            constraints: PxConstraints2d::new_fill_size(test.align_size()),
4796            inline_constraints: None,
4797            align: test.align(),
4798            overflow_align: test.overflow_align(),
4799            direction: test.direction(),
4800            line_height: test.line_height(),
4801            line_spacing: to,
4802            paragraph_spacing: Px(0),
4803            paragraph_indent: (Px(0), false),
4804            paragraph_break: ParagraphBreak::None,
4805        });
4806        assert_eq!(to, test.line_spacing());
4807
4808        for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4809            assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4810        }
4811
4812        assert_eq!(test.size(), expected.size());
4813    }
4814
4815    #[test]
4816    fn set_line_height() {
4817        let text = "0\n1\n2\n3\n4";
4818        test_line_height(text, Px(20), Px(20));
4819        test_line_height(text, Px(20), Px(10));
4820        test_line_height(text, Px(10), Px(20));
4821        test_line_height("a line\nanother\nand another", Px(20), Px(10));
4822        test_line_height("", Px(20), Px(10));
4823        test_line_height("a line", Px(20), Px(10));
4824    }
4825    fn test_line_height(text: &'static str, from: Px, to: Px) {
4826        let font = test_font();
4827        let mut config = TextShapingArgs {
4828            line_height: from,
4829            line_spacing: Px(20),
4830            ..Default::default()
4831        };
4832
4833        let text = SegmentedText::new(text, LayoutDirection::LTR);
4834        let mut test = font.shape_text(&text, &config);
4835
4836        config.line_height = to;
4837        let expected = font.shape_text(&text, &config);
4838
4839        assert_eq!(from, test.line_height());
4840        test.reshape_lines(&TextReshapingArgs {
4841            constraints: PxConstraints2d::new_fill_size(test.align_size()),
4842            inline_constraints: None,
4843            align: test.align(),
4844            overflow_align: test.overflow_align(),
4845            direction: test.direction(),
4846            line_height: to,
4847            line_spacing: test.line_spacing(),
4848            paragraph_spacing: Px(0),
4849            paragraph_indent: (Px(0), false),
4850            paragraph_break: ParagraphBreak::None,
4851        });
4852        assert_eq!(to, test.line_height());
4853
4854        for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4855            assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4856        }
4857
4858        assert_eq!(test.size(), expected.size());
4859    }
4860
4861    #[test]
4862    fn font_fallback_issue() {
4863        let mut app = APP.minimal().run_headless(false);
4864        app.run_test(async {
4865            let font = FONTS
4866                .list(
4867                    &[FontName::new("Consolas"), FontName::monospace()],
4868                    FontStyle::Normal,
4869                    FontWeight::NORMAL,
4870                    FontStretch::NORMAL,
4871                    &lang!(und),
4872                )
4873                .wait_rsp()
4874                .await
4875                .sized(Px(20), vec![]);
4876
4877            let config = TextShapingArgs::default();
4878
4879            let txt_seg = SegmentedText::new("النص ثنائي الاتجاه (بالإنجليزية:Bi", LayoutDirection::RTL);
4880            let txt_shape = font.shape_text(&txt_seg, &config);
4881
4882            let _ok = (txt_seg, txt_shape);
4883        })
4884        .unwrap()
4885    }
4886
4887    #[test]
4888    fn cluster_is_byte() {
4889        let font = test_font();
4890
4891        let data = font.shape_segment_no_cache("£a", &WordContextKey::new(&lang!("en-US"), LayoutDirection::LTR, &vec![]), &[]);
4892
4893        for ((i, _), g) in "£a".char_indices().zip(&data.glyphs) {
4894            assert_eq!(i as u32, g.cluster);
4895        }
4896    }
4897}