Skip to main content

fonts/
glyph.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::fmt;
6use std::ops::Range;
7use std::sync::Arc;
8use std::vec::Vec;
9
10use app_units::Au;
11use euclid::default::Point2D;
12use euclid::num::Zero;
13use itertools::Either;
14use log::{debug, error};
15use malloc_size_of_derive::MallocSizeOf;
16use serde::{Deserialize, Serialize};
17
18use crate::{GlyphShapingResult, ShapedGlyph, ShapingOptions};
19
20/// GlyphEntry is a port of Gecko's CompressedGlyph scheme for storing glyph data compactly.
21///
22/// In the common case (reasonable glyph advances, no offsets from the font em-box, and one glyph
23/// per character), we pack glyph advance, glyph id, and some flags into a single u32.
24///
25/// In the uncommon case (multiple glyphs per unicode character, large glyph index/advance, or glyph
26/// offsets), we create a DetailedGlyphEntry for the glyph and pack its index into the GlyphEntry.
27#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
28pub struct GlyphEntry {
29    value: u32,
30}
31
32impl GlyphEntry {
33    fn new(value: u32) -> GlyphEntry {
34        GlyphEntry { value }
35    }
36
37    // Creates a GlyphEntry for the common case
38    fn simple(id: GlyphId, advance: Au) -> GlyphEntry {
39        assert!(is_simple_glyph_id(id));
40        assert!(is_simple_advance(advance));
41
42        let id_mask = id;
43        let Au(advance) = advance;
44        let advance_mask = (advance as u32) << GLYPH_ADVANCE_SHIFT;
45
46        GlyphEntry::new(id_mask | advance_mask | FLAG_IS_SIMPLE_GLYPH)
47    }
48
49    fn complex(detailed_glyph_index: usize) -> GlyphEntry {
50        assert!(detailed_glyph_index as u32 <= u32::MAX >> 1);
51        GlyphEntry::new(detailed_glyph_index as u32)
52    }
53}
54
55/// The id of a particular glyph within a font
56pub(crate) type GlyphId = u32;
57
58// TODO: make this more type-safe.
59
60const FLAG_CHAR_IS_WORD_SEPARATOR: u32 = 0x40000000;
61const FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000;
62
63// glyph advance; in Au's.
64const GLYPH_ADVANCE_MASK: u32 = 0x3FFF0000;
65const GLYPH_ADVANCE_SHIFT: u32 = 16;
66const GLYPH_ID_MASK: u32 = 0x0000FFFF;
67
68// Non-simple glyphs (more than one glyph per char; missing glyph,
69// newline, tab, large advance, or nonzero x/y offsets) may have one
70// or more detailed glyphs associated with them. They are stored in a
71// side array so that there is a 1:1 mapping of GlyphEntry to
72// unicode char.
73
74fn is_simple_glyph_id(id: GlyphId) -> bool {
75    (id & GLYPH_ID_MASK) == id
76}
77
78fn is_simple_advance(advance: Au) -> bool {
79    advance >= Au::zero() && {
80        let unsigned_au = advance.0 as u32;
81        (unsigned_au & (GLYPH_ADVANCE_MASK >> GLYPH_ADVANCE_SHIFT)) == unsigned_au
82    }
83}
84
85// Getters and setters for GlyphEntry. Setter methods are functional,
86// because GlyphEntry is immutable and only a u32 in size.
87impl GlyphEntry {
88    #[inline(always)]
89    fn advance(&self) -> Au {
90        Au::new(((self.value & GLYPH_ADVANCE_MASK) >> GLYPH_ADVANCE_SHIFT) as i32)
91    }
92
93    #[inline]
94    fn id(&self) -> GlyphId {
95        self.value & GLYPH_ID_MASK
96    }
97
98    /// True if the original character was a word separator. These include spaces
99    /// (U+0020), non-breaking spaces (U+00A0), and a few other characters
100    /// non-exhaustively listed in the specification. Other characters may map to the same
101    /// glyphs, but this function does not take mapping into account.
102    ///
103    /// See <https://drafts.csswg.org/css-text/#word-separator>.
104    fn char_is_word_separator(&self) -> bool {
105        self.has_flag(FLAG_CHAR_IS_WORD_SEPARATOR)
106    }
107
108    #[inline(always)]
109    fn set_char_is_word_separator(&mut self) {
110        self.value |= FLAG_CHAR_IS_WORD_SEPARATOR;
111    }
112
113    fn detailed_glyph_index(&self) -> usize {
114        self.value as usize
115    }
116
117    #[inline(always)]
118    fn is_simple(&self) -> bool {
119        self.has_flag(FLAG_IS_SIMPLE_GLYPH)
120    }
121
122    #[inline(always)]
123    fn has_flag(&self, flag: u32) -> bool {
124        (self.value & flag) != 0
125    }
126}
127
128#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
129pub struct DetailedGlyphEntry {
130    /// The id of the this glyph within the font.
131    id: u32,
132    /// The advance that this glyphs needs ie the distance between where this
133    /// glyph is painted and the next is painted.
134    advance: Au,
135    /// The physical offset that this glyph should be painted with.
136    offset: Option<Point2D<Au>>,
137    /// The number of character this glyph corresponds to in the original string.
138    /// This might be zero and this might be more than one.
139    character_count: usize,
140    /// Whether or not the originating character for this glyph was a word separator
141    is_word_separator: bool,
142}
143
144// This enum is a proxy that's provided to ShapedText clients when iterating
145// through glyphs (either for a particular TextRun offset, or all glyphs).
146#[derive(Clone, Copy)]
147pub enum GlyphInfo<'a> {
148    Simple(&'a GlyphEntry),
149    Detail(&'a DetailedGlyphEntry),
150}
151
152impl GlyphInfo<'_> {
153    pub fn id(self) -> GlyphId {
154        match self {
155            GlyphInfo::Simple(entry) => entry.id(),
156            GlyphInfo::Detail(entry) => entry.id,
157        }
158    }
159
160    #[inline(always)]
161    pub fn advance(self) -> Au {
162        match self {
163            GlyphInfo::Simple(entry) => entry.advance(),
164            GlyphInfo::Detail(entry) => entry.advance,
165        }
166    }
167
168    #[inline]
169    pub fn offset(self) -> Option<Point2D<Au>> {
170        match self {
171            GlyphInfo::Simple(..) => None,
172            GlyphInfo::Detail(entry) => entry.offset,
173        }
174    }
175
176    #[inline]
177    pub fn char_is_word_separator(self) -> bool {
178        match self {
179            GlyphInfo::Simple(entry) => entry.char_is_word_separator(),
180            GlyphInfo::Detail(entry) => entry.is_word_separator,
181        }
182    }
183
184    /// The number of characters that this glyph corresponds to. This may be more
185    /// than one when a single glyph is produced for multiple characters. This may
186    /// be zero when multiple glyphs are produced for a single character.
187    #[inline]
188    pub fn character_count(self) -> usize {
189        match self {
190            GlyphInfo::Simple(..) => 1,
191            GlyphInfo::Detail(entry) => entry.character_count,
192        }
193    }
194}
195
196/// Stores the glyph data belonging to a text run.
197///
198/// Simple glyphs are stored inline in the `entry_buffer`, detailed glyphs are
199/// stored as pointers into the `detail_store`.
200///
201/// ~~~ascii
202/// +- ShapedText --------------------------------+
203/// |               +---+---+---+---+---+---+---+ |
204/// | entry_buffer: |   | s |   | s |   | s | s | |  d = detailed
205/// |               +-|-+---+-|-+---+-|-+---+---+ |  s = simple
206/// |                 |       |       |           |
207/// |                 |   +---+-------+           |
208/// |                 |   |                       |
209/// |               +-V-+-V-+                     |
210/// | detail_store: | d | d |                     |
211/// |               +---+---+                     |
212/// +---------------------------------------------+
213/// ~~~
214#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
215pub struct ShapedText {
216    // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
217    // optimization.
218    /// A collection of [`GlyphEntry`]s within the [`ShapedText`]. Each [`GlyphEntry`]
219    /// maybe simple or detailed. When detailed, there will be a corresponding entry
220    /// in [`Self::detailed_glyphs`].
221    glyphs: Vec<GlyphEntry>,
222
223    /// A vector of glyphs that cannot fit within a single [`GlyphEntry`] or that
224    /// correspond to 0 or more than 1 character in the original string.
225    detailed_glyphs: Vec<DetailedGlyphEntry>,
226
227    /// A cache of the advance of the entire glyph store.
228    total_advance: Au,
229
230    /// The number of characters that correspond to the glyphs in this [`ShapedText`]
231    character_count: usize,
232
233    /// A cache of the number of word separators in the entire glyph store.
234    /// See <https://drafts.csswg.org/css-text/#word-separator>.
235    total_word_separators: usize,
236
237    /// Whether or not this [`ShapedText`] has right-to-left text, which has implications
238    /// about the order of the glyphs in the store.
239    is_rtl: bool,
240}
241
242impl ShapedText {
243    /// Initializes the glyph store with the given capacity, but doesn't actually add any glyphs.
244    ///
245    /// Use the `add_*` methods to store glyph data.
246    pub(crate) fn new(length: usize, is_rtl: bool) -> Self {
247        Self {
248            glyphs: Vec::with_capacity(length),
249            detailed_glyphs: Default::default(),
250            total_advance: Au::zero(),
251            character_count: 0,
252            total_word_separators: 0,
253            is_rtl,
254        }
255    }
256
257    /// This constructor turns shaping output from HarfBuzz into a glyph run to be
258    /// used by layout. The idea here is that we add each glyph to the [`ShapedText`]
259    /// and track to which characters from the original string each glyph
260    /// corresponds. HarfBuzz will either give us glyphs that correspond to
261    /// characters left-to-right or right-to-left. Each character can produce
262    /// multiple glyphs and multiple characters can produce one glyph. HarfBuzz just
263    /// guarantees that the resulting character offsets are in monotone order.
264    pub(crate) fn with_shaped_glyph_data(
265        text: &str,
266        options: &ShapingOptions,
267        shaped_glyph_data: &impl GlyphShapingResult,
268    ) -> Self {
269        debug!(
270            "Shaped: '{text:?}: {:?}",
271            shaped_glyph_data.iter().collect::<Vec<_>>()
272        );
273
274        // Note: Even if we set the `RTL_FLAG` in the options, Harfbuzz may still
275        // give us shaped glyphs in left-to-right order. We need to look at the
276        // actual cluster indices in the shaped run.
277        let shaped_run_is_rtl = shaped_glyph_data.is_rtl();
278        let mut characters = if !shaped_run_is_rtl {
279            Either::Left(text.char_indices())
280        } else {
281            Either::Right(text.char_indices().rev())
282        };
283
284        let mut previous_character_offset = None;
285        let mut glyph_store = ShapedText::new(shaped_glyph_data.len(), shaped_run_is_rtl);
286        for mut shaped_glyph in shaped_glyph_data.iter() {
287            // The glyph "cluster" (HarfBuzz terminology) is the byte offset in the string that
288            // this glyph corresponds to. More than one glyph can share a cluster.
289            let glyph_cluster = shaped_glyph.string_byte_offset;
290
291            if let Some(previous_character_offset) = previous_character_offset {
292                if previous_character_offset == glyph_cluster {
293                    glyph_store.add_glyph_for_current_character(&shaped_glyph, options);
294                    continue;
295                }
296            }
297
298            previous_character_offset = Some(glyph_cluster);
299            let mut characters_skipped = 0;
300            let Some(character) = characters.find_map(|(character_offset, character)| {
301                if glyph_cluster == character_offset {
302                    Some(character)
303                } else {
304                    characters_skipped += 1;
305                    None
306                }
307            }) else {
308                error!("HarfBuzz shaping results extended past character count");
309                return glyph_store;
310            };
311
312            shaped_glyph.adjust_for_character(character, options);
313
314            // If the we are working from the end of the string to the start and
315            // characters were skipped to produce this glyph, they belong to this
316            // glyph.
317            if shaped_run_is_rtl {
318                glyph_store.add_glyph(character, &shaped_glyph);
319            }
320
321            for _ in 0..characters_skipped {
322                glyph_store.extend_previous_glyph_by_character()
323            }
324
325            // If the we are working from the estart of the string to the end and
326            // characters were skipped to produce this glyph, they belong to the
327            // previous glyph.
328            if !shaped_run_is_rtl {
329                glyph_store.add_glyph(character, &shaped_glyph);
330            }
331        }
332
333        // Consume any remaining characters that belong to the more-recently added glyph.
334        for (_, _) in characters {
335            glyph_store.extend_previous_glyph_by_character();
336        }
337
338        glyph_store
339    }
340
341    /// Return the number of glyphs stored in this [`ShapedText`].
342    #[inline]
343    pub fn glyph_count(&self) -> usize {
344        self.glyphs.len()
345    }
346
347    /// Adds glyph that corresponds to a single character (as far we know) in the originating string.
348    #[inline]
349    pub(crate) fn add_glyph(&mut self, character: char, glyph: &ShapedGlyph) {
350        if !glyph.can_be_simple_glyph() {
351            self.add_detailed_glyph(glyph, Some(character), 1);
352            return;
353        }
354
355        let mut simple_glyph_entry = GlyphEntry::simple(glyph.glyph_id, glyph.advance);
356        if character_is_word_separator(character) {
357            self.total_word_separators += 1;
358            simple_glyph_entry.set_char_is_word_separator();
359        }
360
361        self.character_count += 1;
362        self.total_advance += glyph.advance;
363        self.glyphs.push(simple_glyph_entry)
364    }
365
366    fn add_detailed_glyph(
367        &mut self,
368        shaped_glyph: &ShapedGlyph,
369        character: Option<char>,
370        character_count: usize,
371    ) {
372        let is_word_separator = character.is_some_and(character_is_word_separator);
373        if is_word_separator {
374            self.total_word_separators += 1;
375        }
376
377        self.character_count += character_count;
378        self.total_advance += shaped_glyph.advance;
379        self.detailed_glyphs.push(DetailedGlyphEntry {
380            id: shaped_glyph.glyph_id,
381            advance: shaped_glyph.advance,
382            offset: shaped_glyph.offset,
383            character_count,
384            is_word_separator,
385        });
386        self.glyphs
387            .push(GlyphEntry::complex(self.detailed_glyphs.len() - 1));
388    }
389
390    fn extend_previous_glyph_by_character(&mut self) {
391        let detailed_glyph_index = self.ensure_last_glyph_is_detailed();
392        let detailed_glyph = self
393            .detailed_glyphs
394            .get_mut(detailed_glyph_index)
395            .expect("GlyphEntry should have valid index to detailed glyph");
396        detailed_glyph.character_count += 1;
397        self.character_count += 1;
398    }
399
400    fn add_glyph_for_current_character(
401        &mut self,
402        shaped_glyph: &ShapedGlyph,
403        options: &ShapingOptions,
404    ) {
405        // If this glyph cluster is extending to include another glyph and we applied
406        // letter spacing to the previous glyph, ensure that the letter spacing is only
407        // applied to the last glyph in the cluster. Note that this is unconditionally
408        // converting the previous glyph to a detailed one because it's quite likely that
409        // the advance will not fit into the simple bitmask due to being negative.
410        if let Some(letter_spacing) = options.letter_spacing {
411            if letter_spacing != Au::zero() {
412                let last_glyph_index = self.ensure_last_glyph_is_detailed();
413                self.detailed_glyphs[last_glyph_index].advance -= letter_spacing;
414            }
415        }
416
417        // Add a detailed glyph entry for this new glyph, but it corresponds to a character
418        // we have already started processing. It should not contribute any character count.
419        self.add_detailed_glyph(shaped_glyph, None, 0);
420    }
421
422    /// If the last glyph added to this [`ShapedText`] was a simple glyph, convert it to a
423    /// detailed one. In either case, return the index into [`Self::detailed_glyphs`] for
424    /// the most recently added glyph.
425    fn ensure_last_glyph_is_detailed(&mut self) -> usize {
426        let last_glyph = self
427            .glyphs
428            .last_mut()
429            .expect("Should never call this before any glyphs have been added.");
430        if !last_glyph.is_simple() {
431            return last_glyph.detailed_glyph_index();
432        }
433
434        self.detailed_glyphs.push(DetailedGlyphEntry {
435            id: last_glyph.id(),
436            advance: last_glyph.advance(),
437            offset: Default::default(),
438            character_count: 1,
439            is_word_separator: last_glyph.char_is_word_separator(),
440        });
441
442        let detailed_glyph_index = self.detailed_glyphs.len() - 1;
443        *last_glyph = GlyphEntry::complex(detailed_glyph_index);
444        detailed_glyph_index
445    }
446
447    pub fn glyphs(&self) -> impl DoubleEndedIterator<Item = GlyphInfo<'_>> + use<'_> {
448        self.glyph_slice(0..self.glyphs.len())
449    }
450
451    fn glyph_slice(
452        &self,
453        glyph_range: Range<usize>,
454    ) -> impl DoubleEndedIterator<Item = GlyphInfo<'_>> + use<'_> {
455        self.glyphs[glyph_range].iter().map(|entry| {
456            if entry.is_simple() {
457                GlyphInfo::Simple(entry)
458            } else {
459                GlyphInfo::Detail(&self.detailed_glyphs[entry.detailed_glyph_index()])
460            }
461        })
462    }
463}
464
465impl ShapedGlyph {
466    fn can_be_simple_glyph(&self) -> bool {
467        is_simple_glyph_id(self.glyph_id) &&
468            is_simple_advance(self.advance) &&
469            self.offset
470                .is_none_or(|offset| offset == Default::default())
471    }
472
473    /// After shaping is complete, some glyphs need their spacing adjusted to take into
474    /// account `letter-spacing` and `word-spacing`.
475    pub(crate) fn adjust_for_character(
476        &mut self,
477        character: char,
478        shaping_options: &ShapingOptions,
479    ) {
480        if let Some(letter_spacing) = shaping_options.letter_spacing_for_character(character) {
481            self.advance += letter_spacing;
482        };
483
484        // CSS 2.1 ยง 16.4 states that "word spacing affects each space (U+0020) and non-breaking
485        // space (U+00A0) left in the text after the white space processing rules have been
486        // applied. The effect of the property on other word-separator characters is undefined."
487        // We elect to only space the two required code points.
488        if let Some(word_spacing) = shaping_options.word_spacing {
489            if character == ' ' || character == '\u{a0}' {
490                // https://drafts.csswg.org/css-text-3/#word-spacing-property
491                self.advance += word_spacing;
492            }
493        }
494    }
495}
496
497fn character_is_word_separator(character: char) -> bool {
498    // This list is taken from the non-exhaustive list of word separator characters in
499    // the CSS Text Module Level 3 Spec:
500    // See https://drafts.csswg.org/css-text/#word-separator
501    let is_word_separator = matches!(
502        character,
503        ' ' |
504                '\u{00A0}' | // non-breaking space
505                '\u{1361}' | // Ethiopic word space
506                '\u{10100}' | // Aegean word separator
507                '\u{10101}' | // Aegean word separator
508                '\u{1039F}' | // Ugartic word divider
509                '\u{1091F}' // Phoenician word separator
510    );
511    is_word_separator
512}
513
514impl fmt::Debug for ShapedText {
515    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
516        writeln!(formatter, "ShapedText:")?;
517        for entry in self.glyphs.iter() {
518            if entry.is_simple() {
519                writeln!(
520                    formatter,
521                    "  simple id={:?} advance={:?}",
522                    entry.id(),
523                    entry.advance()
524                )?;
525                continue;
526            } else {
527                let detailed_glyph = &self.detailed_glyphs[entry.detailed_glyph_index()];
528                writeln!(
529                    formatter,
530                    "  detailed id={:?} advance={:?} characters={:?}",
531                    detailed_glyph.id, detailed_glyph.advance, detailed_glyph.character_count,
532                )?;
533            }
534        }
535        Ok(())
536    }
537}
538
539/// A slice of a [`ShapedText`] which allows having different views into a shaped
540/// text run. This is used for splitting up shaped text during layout, without
541/// duplicating the entire run.
542#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
543pub struct ShapedTextSlice {
544    /// The [`ShapedText`] that this [`ShapedTextSlice`] refers to.
545    #[conditional_malloc_size_of]
546    shaped_text: Arc<ShapedText>,
547
548    /// The range of glyphs within the [`ShapedText`] that this [`ShapedTextSlice`] represents.
549    glyph_range: Range<usize>,
550
551    /// A cache of the advance of the entire [`ShapedTextSlice`].
552    total_advance: Au,
553
554    /// The number of characters that correspond to the glyphs in this [`ShapedTextSlice`]
555    character_count: usize,
556
557    /// A precomputed count of the number of word separators in the entire [`ShapedTextSlice`]. See
558    /// <https://drafts.csswg.org/css-text/#word-separator>.
559    total_word_separators: usize,
560
561    /// Whether or not this [`ShapedTextSlice`] is entirely whitespace.
562    is_whitespace: bool,
563
564    /// Whether or not this [`ShapedTextSlice`] ends with whitespace glyphs.
565    /// Typically whitespace glyphs are placed in a separate slice,
566    /// but that may not be the case with `white-space: break-spaces`.
567    ends_with_whitespace: bool,
568}
569
570impl ShapedTextSlice {
571    /// Return the [`ShapedText`] that backs this [`ShapedTextSlice`].
572    #[inline]
573    pub fn shaped_text(&self) -> Arc<ShapedText> {
574        self.shaped_text.clone()
575    }
576
577    /// Return the number of glyphs represented by this [`ShapedTextSlice`].
578    #[inline]
579    pub fn glyph_count(&self) -> usize {
580        self.glyph_range.len()
581    }
582
583    /// The number of characters that were consumed to produce this [`ShapedTextSlice`]. Some
584    /// characters correspond to more than one glyph and some glyphs correspond to more than
585    /// one character.
586    #[inline]
587    pub fn character_count(&self) -> usize {
588        self.character_count
589    }
590
591    /// The total advance of the characters represented by this [`ShapedTextSlice`].
592    #[inline]
593    pub fn total_advance(&self) -> Au {
594        self.total_advance
595    }
596
597    /// The number of word separators in this [`ShapedTextSlice`].
598    #[inline]
599    pub fn total_word_separators(&self) -> usize {
600        self.total_word_separators
601    }
602
603    /// Whether or not this [`ShapedTextSlice`] is entirely whitespace.
604    #[inline]
605    pub fn is_whitespace(&self) -> bool {
606        self.is_whitespace
607    }
608
609    /// Whether or not this [`ShapedTextSlice`] ends with whitespace.
610    #[inline]
611    pub fn ends_with_whitespace(&self) -> bool {
612        self.ends_with_whitespace
613    }
614
615    /// An iterator over the glyphs represented by this [`ShapedTextSlice`].
616    pub fn glyphs(&self) -> impl DoubleEndedIterator<Item = GlyphInfo<'_>> + use<'_> {
617        self.shaped_text.glyph_slice(self.glyph_range.clone())
618    }
619}
620
621/// A data structure used to efficiently slice up a [`ShapedText`] into [`ShapedTextSlice`]s.
622pub struct ShapedTextSlicer {
623    current_glyph_offset: usize,
624    shaped_text: Arc<ShapedText>,
625}
626
627impl ShapedTextSlicer {
628    pub fn new(shaped_text: Arc<ShapedText>) -> Self {
629        let current_glyph_offset = if shaped_text.is_rtl {
630            shaped_text.glyph_count()
631        } else {
632            0
633        };
634
635        Self {
636            current_glyph_offset,
637            shaped_text,
638        }
639    }
640
641    /// Given a desired character count, consume that number of characters worth
642    /// of glyphs from the [`ShapedText`] of this [`ShapedTextSlicer`]. In addition,
643    /// tag the resulting [`ShapedTextSlice`] with the given whitespace-related
644    /// properties.
645    pub fn slice_for_character_count(
646        &mut self,
647        desired_character_count: usize,
648        is_whitespace: bool,
649        ends_with_whitespace: bool,
650    ) -> Arc<ShapedTextSlice> {
651        let mut glyph_count = 0;
652        let mut character_count = 0;
653        let mut total_word_separators = 0;
654        let mut total_advance = Au::zero();
655
656        // In `ShapedText` glyphs are stored in physical left-to-right order, which means that the
657        // indices of the characters that they represent might decrease. Since we want to consume
658        // characters in memory order, we may need to walk the glyphs in the `ShapedText` from right
659        // to left.
660        let iterator = if self.shaped_text.is_rtl {
661            Either::Left(
662                self.shaped_text
663                    .glyph_slice(0..self.current_glyph_offset)
664                    .rev(),
665            )
666        } else {
667            Either::Right(
668                self.shaped_text
669                    .glyph_slice(self.current_glyph_offset..self.shaped_text.glyph_count()),
670            )
671        };
672
673        for glyph in iterator {
674            // When one character produces multiple glyphs, only the first glyph has a character
675            // count of one and the rest have a character count of 0. This checks the potential
676            // total before accumulating a glyph, which allows merging glyphs with a 0 character
677            // count into the slice with the first glyph.
678            let glyph_character_count = glyph.character_count();
679            if character_count + glyph_character_count > desired_character_count {
680                break;
681            }
682
683            glyph_count += 1;
684            character_count += glyph_character_count;
685            total_advance += glyph.advance();
686            if glyph.char_is_word_separator() {
687                total_word_separators += 1;
688            }
689        }
690
691        let (new_glyph_offset, glyph_range) = if self.shaped_text.is_rtl {
692            assert!(self.current_glyph_offset >= glyph_count);
693            let new_glyph_offset = self.current_glyph_offset - glyph_count;
694            (
695                new_glyph_offset,
696                new_glyph_offset..self.current_glyph_offset,
697            )
698        } else {
699            let new_glyph_offset = self.current_glyph_offset + glyph_count;
700            (
701                new_glyph_offset,
702                self.current_glyph_offset..new_glyph_offset,
703            )
704        };
705
706        self.current_glyph_offset = new_glyph_offset;
707        Arc::new(ShapedTextSlice {
708            shaped_text: self.shaped_text.clone(),
709            glyph_range,
710            total_advance,
711            character_count,
712            is_whitespace,
713            ends_with_whitespace,
714            total_word_separators,
715        })
716    }
717}