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