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::cmp::{Ordering, PartialOrd};
6use std::iter::once;
7use std::sync::Arc;
8use std::vec::Vec;
9use std::{fmt, mem};
10
11use app_units::Au;
12use euclid::default::Point2D;
13use euclid::num::Zero;
14pub(crate) use fonts_traits::ByteIndex;
15use itertools::Either;
16use log::debug;
17use malloc_size_of_derive::MallocSizeOf;
18use range::{self, Range, RangeIndex};
19use serde::{Deserialize, Serialize};
20
21/// GlyphEntry is a port of Gecko's CompressedGlyph scheme for storing glyph data compactly.
22///
23/// In the common case (reasonable glyph advances, no offsets from the font em-box, and one glyph
24/// per character), we pack glyph advance, glyph id, and some flags into a single u32.
25///
26/// In the uncommon case (multiple glyphs per unicode character, large glyph index/advance, or
27/// glyph offsets), we pack the glyph count into GlyphEntry, and store the other glyph information
28/// in DetailedGlyphStore.
29#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
30pub(crate) struct GlyphEntry {
31    value: u32,
32}
33
34impl GlyphEntry {
35    fn new(value: u32) -> GlyphEntry {
36        GlyphEntry { value }
37    }
38
39    fn initial() -> GlyphEntry {
40        GlyphEntry::new(0)
41    }
42
43    // Creates a GlyphEntry for the common case
44    fn simple(id: GlyphId, advance: Au) -> GlyphEntry {
45        assert!(is_simple_glyph_id(id));
46        assert!(is_simple_advance(advance));
47
48        let id_mask = id;
49        let Au(advance) = advance;
50        let advance_mask = (advance as u32) << GLYPH_ADVANCE_SHIFT;
51
52        GlyphEntry::new(id_mask | advance_mask | FLAG_IS_SIMPLE_GLYPH)
53    }
54
55    // Create a GlyphEntry for uncommon case; should be accompanied by
56    // initialization of the actual DetailedGlyph data in DetailedGlyphStore
57    fn complex(starts_cluster: bool, starts_ligature: bool, glyph_count: usize) -> GlyphEntry {
58        assert!(glyph_count <= u16::MAX as usize);
59
60        debug!(
61            "creating complex glyph entry: starts_cluster={}, starts_ligature={}, \
62             glyph_count={}",
63            starts_cluster, starts_ligature, glyph_count
64        );
65
66        GlyphEntry::new(glyph_count as u32)
67    }
68
69    fn is_initial(&self) -> bool {
70        *self == GlyphEntry::initial()
71    }
72}
73
74/// The id of a particular glyph within a font
75pub(crate) type GlyphId = u32;
76
77// TODO: make this more type-safe.
78
79const FLAG_CHAR_IS_WORD_SEPARATOR: u32 = 0x40000000;
80const FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000;
81
82// glyph advance; in Au's.
83const GLYPH_ADVANCE_MASK: u32 = 0x3FFF0000;
84const GLYPH_ADVANCE_SHIFT: u32 = 16;
85const GLYPH_ID_MASK: u32 = 0x0000FFFF;
86
87// Non-simple glyphs (more than one glyph per char; missing glyph,
88// newline, tab, large advance, or nonzero x/y offsets) may have one
89// or more detailed glyphs associated with them. They are stored in a
90// side array so that there is a 1:1 mapping of GlyphEntry to
91// unicode char.
92
93// The number of detailed glyphs for this char.
94const GLYPH_COUNT_MASK: u32 = 0x0000FFFF;
95
96fn is_simple_glyph_id(id: GlyphId) -> bool {
97    (id & GLYPH_ID_MASK) == id
98}
99
100fn is_simple_advance(advance: Au) -> bool {
101    advance >= Au::zero() && {
102        let unsigned_au = advance.0 as u32;
103        (unsigned_au & (GLYPH_ADVANCE_MASK >> GLYPH_ADVANCE_SHIFT)) == unsigned_au
104    }
105}
106
107// Getters and setters for GlyphEntry. Setter methods are functional,
108// because GlyphEntry is immutable and only a u32 in size.
109impl GlyphEntry {
110    #[inline(always)]
111    fn advance(&self) -> Au {
112        Au::new(((self.value & GLYPH_ADVANCE_MASK) >> GLYPH_ADVANCE_SHIFT) as i32)
113    }
114
115    #[inline]
116    fn id(&self) -> GlyphId {
117        self.value & GLYPH_ID_MASK
118    }
119
120    /// True if the original character was a word separator. These include spaces
121    /// (U+0020), non-breaking spaces (U+00A0), and a few other characters
122    /// non-exhaustively listed in the specification. Other characters may map to the same
123    /// glyphs, but this function does not take mapping into account.
124    ///
125    /// See <https://drafts.csswg.org/css-text/#word-separator>.
126    fn char_is_word_separator(&self) -> bool {
127        self.has_flag(FLAG_CHAR_IS_WORD_SEPARATOR)
128    }
129
130    #[inline(always)]
131    fn set_char_is_word_separator(&mut self) {
132        self.value |= FLAG_CHAR_IS_WORD_SEPARATOR;
133    }
134
135    fn glyph_count(&self) -> u16 {
136        assert!(!self.is_simple());
137        (self.value & GLYPH_COUNT_MASK) as u16
138    }
139
140    #[inline(always)]
141    fn is_simple(&self) -> bool {
142        self.has_flag(FLAG_IS_SIMPLE_GLYPH)
143    }
144
145    #[inline(always)]
146    fn has_flag(&self, flag: u32) -> bool {
147        (self.value & flag) != 0
148    }
149}
150
151// Stores data for a detailed glyph, in the case that several glyphs
152// correspond to one character, or the glyph's data couldn't be packed.
153#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
154struct DetailedGlyph {
155    id: GlyphId,
156    // glyph's advance, in the text's direction (LTR or RTL)
157    advance: Au,
158    // glyph's offset from the font's em-box (from top-left)
159    offset: Point2D<Au>,
160}
161
162impl DetailedGlyph {
163    fn new(id: GlyphId, advance: Au, offset: Point2D<Au>) -> DetailedGlyph {
164        DetailedGlyph {
165            id,
166            advance,
167            offset,
168        }
169    }
170}
171
172#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
173struct DetailedGlyphRecord {
174    // source string offset/GlyphEntry offset in the TextRun
175    entry_offset: ByteIndex,
176    // offset into the detailed glyphs buffer
177    detail_offset: usize,
178}
179
180impl Ord for DetailedGlyphRecord {
181    fn cmp(&self, other: &DetailedGlyphRecord) -> Ordering {
182        self.entry_offset.cmp(&other.entry_offset)
183    }
184}
185
186impl PartialOrd for DetailedGlyphRecord {
187    fn partial_cmp(&self, other: &DetailedGlyphRecord) -> Option<Ordering> {
188        Some(self.cmp(other))
189    }
190}
191
192// Manages the lookup table for detailed glyphs. Sorting is deferred
193// until a lookup is actually performed; this matches the expected
194// usage pattern of setting/appending all the detailed glyphs, and
195// then querying without setting.
196#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
197struct DetailedGlyphStore {
198    // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
199    // optimization.
200    detail_buffer: Vec<DetailedGlyph>,
201    // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
202    // optimization.
203    detail_lookup: Vec<DetailedGlyphRecord>,
204    lookup_is_sorted: bool,
205}
206
207impl<'a> DetailedGlyphStore {
208    fn new() -> DetailedGlyphStore {
209        DetailedGlyphStore {
210            detail_buffer: vec![], // TODO: default size?
211            detail_lookup: vec![],
212            lookup_is_sorted: false,
213        }
214    }
215
216    fn add_detailed_glyphs_for_entry(&mut self, entry_offset: ByteIndex, glyphs: &[DetailedGlyph]) {
217        let entry = DetailedGlyphRecord {
218            entry_offset,
219            detail_offset: self.detail_buffer.len(),
220        };
221
222        debug!(
223            "Adding entry[off={:?}] for detailed glyphs: {:?}",
224            entry_offset, glyphs
225        );
226
227        debug_assert!(!self.detail_lookup.contains(&entry));
228        self.detail_lookup.push(entry);
229        self.detail_buffer.extend_from_slice(glyphs);
230        self.lookup_is_sorted = false;
231    }
232
233    fn detailed_glyphs_for_entry(
234        &'a self,
235        entry_offset: ByteIndex,
236        count: u16,
237    ) -> &'a [DetailedGlyph] {
238        debug!(
239            "Requesting detailed glyphs[n={}] for entry[off={:?}]",
240            count, entry_offset
241        );
242
243        // FIXME: Is this right? --pcwalton
244        // TODO: should fix this somewhere else
245        if count == 0 {
246            return &self.detail_buffer[0..0];
247        }
248
249        assert!((count as usize) <= self.detail_buffer.len());
250        assert!(self.lookup_is_sorted);
251
252        let key = DetailedGlyphRecord {
253            entry_offset,
254            detail_offset: 0, // unused
255        };
256
257        let i = self
258            .detail_lookup
259            .binary_search(&key)
260            .expect("Invalid index not found in detailed glyph lookup table!");
261        let main_detail_offset = self.detail_lookup[i].detail_offset;
262        assert!(main_detail_offset + (count as usize) <= self.detail_buffer.len());
263        // return a slice into the buffer
264        &self.detail_buffer[main_detail_offset..main_detail_offset + count as usize]
265    }
266
267    fn detailed_glyph_with_index(
268        &'a self,
269        entry_offset: ByteIndex,
270        detail_offset: u16,
271    ) -> &'a DetailedGlyph {
272        assert!((detail_offset as usize) <= self.detail_buffer.len());
273        assert!(self.lookup_is_sorted);
274
275        let key = DetailedGlyphRecord {
276            entry_offset,
277            detail_offset: 0, // unused
278        };
279
280        let i = self
281            .detail_lookup
282            .binary_search(&key)
283            .expect("Invalid index not found in detailed glyph lookup table!");
284        let main_detail_offset = self.detail_lookup[i].detail_offset;
285        assert!(main_detail_offset + (detail_offset as usize) < self.detail_buffer.len());
286        &self.detail_buffer[main_detail_offset + (detail_offset as usize)]
287    }
288
289    fn ensure_sorted(&mut self) {
290        if self.lookup_is_sorted {
291            return;
292        }
293
294        // Sorting a unique vector is surprisingly hard. The following
295        // code is a good argument for using DVecs, but they require
296        // immutable locations thus don't play well with freezing.
297
298        // Thar be dragons here. You have been warned. (Tips accepted.)
299        let mut unsorted_records: Vec<DetailedGlyphRecord> = vec![];
300        mem::swap(&mut self.detail_lookup, &mut unsorted_records);
301        let mut mut_records: Vec<DetailedGlyphRecord> = unsorted_records;
302        mut_records.sort_by(|a, b| {
303            if a < b {
304                Ordering::Less
305            } else {
306                Ordering::Greater
307            }
308        });
309        let mut sorted_records = mut_records;
310        mem::swap(&mut self.detail_lookup, &mut sorted_records);
311
312        self.lookup_is_sorted = true;
313    }
314}
315
316// This struct is used by GlyphStore clients to provide new glyph data.
317// It should be allocated on the stack and passed by reference to GlyphStore.
318#[derive(Clone, Copy, Debug)]
319pub(crate) struct GlyphData {
320    id: GlyphId,
321    advance: Au,
322    offset: Point2D<Au>,
323    cluster_start: bool,
324    ligature_start: bool,
325}
326
327impl GlyphData {
328    /// Creates a new entry for one glyph.
329    pub(crate) fn new(
330        id: GlyphId,
331        advance: Au,
332        offset: Option<Point2D<Au>>,
333        cluster_start: bool,
334        ligature_start: bool,
335    ) -> GlyphData {
336        GlyphData {
337            id,
338            advance,
339            offset: offset.unwrap_or(Point2D::zero()),
340            cluster_start,
341            ligature_start,
342        }
343    }
344}
345
346// This enum is a proxy that's provided to GlyphStore clients when iterating
347// through glyphs (either for a particular TextRun offset, or all glyphs).
348// Rather than eagerly assembling and copying glyph data, it only retrieves
349// values as they are needed from the GlyphStore, using provided offsets.
350#[derive(Clone, Copy)]
351pub enum GlyphInfo<'a> {
352    Simple(&'a GlyphStore, ByteIndex),
353    Detail(&'a GlyphStore, ByteIndex, u16),
354}
355
356impl GlyphInfo<'_> {
357    pub fn id(self) -> GlyphId {
358        match self {
359            GlyphInfo::Simple(store, entry_i) => store.entry_buffer[entry_i.to_usize()].id(),
360            GlyphInfo::Detail(store, entry_i, detail_j) => {
361                store
362                    .detail_store
363                    .detailed_glyph_with_index(entry_i, detail_j)
364                    .id
365            },
366        }
367    }
368
369    #[inline(always)]
370    pub fn advance(self) -> Au {
371        match self {
372            GlyphInfo::Simple(store, entry_i) => store.entry_buffer[entry_i.to_usize()].advance(),
373            GlyphInfo::Detail(store, entry_i, detail_j) => {
374                store
375                    .detail_store
376                    .detailed_glyph_with_index(entry_i, detail_j)
377                    .advance
378            },
379        }
380    }
381
382    #[inline]
383    pub fn offset(self) -> Option<Point2D<Au>> {
384        match self {
385            GlyphInfo::Simple(_, _) => None,
386            GlyphInfo::Detail(store, entry_i, detail_j) => Some(
387                store
388                    .detail_store
389                    .detailed_glyph_with_index(entry_i, detail_j)
390                    .offset,
391            ),
392        }
393    }
394
395    pub fn char_is_word_separator(self) -> bool {
396        let (store, entry_i) = match self {
397            GlyphInfo::Simple(store, entry_i) => (store, entry_i),
398            GlyphInfo::Detail(store, entry_i, _) => (store, entry_i),
399        };
400
401        store.char_is_word_separator(entry_i)
402    }
403}
404
405/// Stores the glyph data belonging to a text run.
406///
407/// Simple glyphs are stored inline in the `entry_buffer`, detailed glyphs are
408/// stored as pointers into the `detail_store`.
409///
410/// ~~~ascii
411/// +- GlyphStore --------------------------------+
412/// |               +---+---+---+---+---+---+---+ |
413/// | entry_buffer: |   | s |   | s |   | s | s | |  d = detailed
414/// |               +-|-+---+-|-+---+-|-+---+---+ |  s = simple
415/// |                 |       |       |           |
416/// |                 |   +---+-------+           |
417/// |                 |   |                       |
418/// |               +-V-+-V-+                     |
419/// | detail_store: | d | d |                     |
420/// |               +---+---+                     |
421/// +---------------------------------------------+
422/// ~~~
423#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
424pub struct GlyphStore {
425    // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
426    // optimization.
427    /// A buffer of glyphs within the text run, in the order in which they
428    /// appear in the input text.
429    /// Any changes will also need to be reflected in
430    /// transmute_entry_buffer_to_u32_buffer().
431    entry_buffer: Vec<GlyphEntry>,
432    /// A store of the detailed glyph data. Detailed glyphs contained in the
433    /// `entry_buffer` point to locations in this data structure.
434    detail_store: DetailedGlyphStore,
435
436    /// A cache of the advance of the entire glyph store.
437    total_advance: Au,
438
439    /// A cache of the number of word separators in the entire glyph store.
440    /// See <https://drafts.csswg.org/css-text/#word-separator>.
441    total_word_separators: usize,
442
443    /// Used to check if fast path should be used in glyph iteration.
444    has_detailed_glyphs: bool,
445
446    /// Whether or not this glyph store contains only glyphs for whitespace.
447    is_whitespace: bool,
448
449    /// Whether or not this glyph store ends with whitespace glyphs.
450    /// Typically whitespace glyphs are placed in a separate store,
451    /// but that may not be the case with `white-space: break-spaces`.
452    ends_with_whitespace: bool,
453
454    /// Whether or not this glyph store contains only a single glyph for a single
455    /// preserved newline.
456    is_single_preserved_newline: bool,
457
458    is_rtl: bool,
459}
460
461impl GlyphStore {
462    /// Initializes the glyph store, but doesn't actually shape anything.
463    ///
464    /// Use the `add_*` methods to store glyph data.
465    pub(crate) fn new(
466        length: usize,
467        is_whitespace: bool,
468        ends_with_whitespace: bool,
469        is_single_preserved_newline: bool,
470        is_rtl: bool,
471    ) -> GlyphStore {
472        assert!(length > 0);
473
474        GlyphStore {
475            entry_buffer: vec![GlyphEntry::initial(); length],
476            detail_store: DetailedGlyphStore::new(),
477            total_advance: Au::zero(),
478            total_word_separators: 0,
479            has_detailed_glyphs: false,
480            is_whitespace,
481            ends_with_whitespace,
482            is_single_preserved_newline,
483            is_rtl,
484        }
485    }
486
487    #[inline]
488    pub fn total_advance(&self) -> Au {
489        self.total_advance
490    }
491
492    #[inline]
493    pub fn len(&self) -> ByteIndex {
494        ByteIndex(self.entry_buffer.len() as isize)
495    }
496
497    #[inline]
498    pub fn is_whitespace(&self) -> bool {
499        self.is_whitespace
500    }
501
502    #[inline]
503    pub fn ends_with_whitespace(&self) -> bool {
504        self.ends_with_whitespace
505    }
506
507    #[inline]
508    pub fn total_word_separators(&self) -> usize {
509        self.total_word_separators
510    }
511
512    pub(crate) fn finalize_changes(&mut self) {
513        self.detail_store.ensure_sorted();
514        self.cache_total_advance_and_word_separators()
515    }
516
517    #[inline(never)]
518    fn cache_total_advance_and_word_separators(&mut self) {
519        let mut total_advance = Au::zero();
520        let mut total_word_separators = 0;
521        for glyph in self.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), self.len())) {
522            total_advance += glyph.advance();
523            if glyph.char_is_word_separator() {
524                total_word_separators += 1;
525            }
526        }
527        self.total_advance = total_advance;
528        self.total_word_separators = total_word_separators;
529    }
530
531    /// Adds a single glyph.
532    pub(crate) fn add_glyph_for_byte_index(
533        &mut self,
534        i: ByteIndex,
535        character: char,
536        data: &GlyphData,
537    ) {
538        let glyph_is_compressible = is_simple_glyph_id(data.id) &&
539            is_simple_advance(data.advance) &&
540            data.offset == Point2D::zero() &&
541            data.cluster_start; // others are stored in detail buffer
542
543        debug_assert!(data.ligature_start); // can't compress ligature continuation glyphs.
544        debug_assert!(i < self.len());
545
546        let mut entry = if glyph_is_compressible {
547            GlyphEntry::simple(data.id, data.advance)
548        } else {
549            let glyph = &[DetailedGlyph::new(data.id, data.advance, data.offset)];
550            self.has_detailed_glyphs = true;
551            self.detail_store.add_detailed_glyphs_for_entry(i, glyph);
552            GlyphEntry::complex(data.cluster_start, data.ligature_start, 1)
553        };
554
555        // This list is taken from the non-exhaustive list of word separator characters in
556        // the CSS Text Module Level 3 Spec:
557        // See https://drafts.csswg.org/css-text/#word-separator
558        if matches!(
559            character,
560            ' ' |
561            '\u{00A0}' | // non-breaking space
562            '\u{1361}' | // Ethiopic word space
563            '\u{10100}' | // Aegean word separator
564            '\u{10101}' | // Aegean word separator
565            '\u{1039F}' | // Ugartic word divider
566            '\u{1091F}' // Phoenician word separator
567        ) {
568            entry.set_char_is_word_separator();
569        }
570
571        self.entry_buffer[i.to_usize()] = entry;
572    }
573
574    pub(crate) fn add_glyphs_for_byte_index(
575        &mut self,
576        i: ByteIndex,
577        data_for_glyphs: &[GlyphData],
578    ) {
579        assert!(i < self.len());
580        assert!(!data_for_glyphs.is_empty());
581
582        let glyph_count = data_for_glyphs.len();
583
584        let first_glyph_data = data_for_glyphs[0];
585        let glyphs_vec: Vec<DetailedGlyph> = (0..glyph_count)
586            .map(|i| {
587                DetailedGlyph::new(
588                    data_for_glyphs[i].id,
589                    data_for_glyphs[i].advance,
590                    data_for_glyphs[i].offset,
591                )
592            })
593            .collect();
594
595        self.has_detailed_glyphs = true;
596        self.detail_store
597            .add_detailed_glyphs_for_entry(i, &glyphs_vec);
598
599        let entry = GlyphEntry::complex(
600            first_glyph_data.cluster_start,
601            first_glyph_data.ligature_start,
602            glyph_count,
603        );
604
605        debug!(
606            "Adding multiple glyphs[idx={:?}, count={}]: {:?}",
607            i, glyph_count, entry
608        );
609
610        self.entry_buffer[i.to_usize()] = entry;
611    }
612
613    #[inline]
614    pub fn iter_glyphs_for_byte_range(
615        &self,
616        range: &Range<ByteIndex>,
617    ) -> impl Iterator<Item = GlyphInfo<'_>> + use<'_> {
618        if range.begin() >= self.len() {
619            panic!("iter_glyphs_for_range: range.begin beyond length!");
620        }
621        if range.end() > self.len() {
622            panic!("iter_glyphs_for_range: range.end beyond length!");
623        }
624
625        let range_it = if self.is_rtl {
626            Either::Left(range.each_index().rev())
627        } else {
628            Either::Right(range.each_index())
629        };
630        range_it.into_iter().flat_map(move |range_idx| {
631            let entry = self.entry_buffer[range_idx.to_usize()];
632            let result = if entry.is_simple() {
633                Either::Left(once(GlyphInfo::Simple(self, range_idx)))
634            } else {
635                // Slow path for complex glyphs
636                let glyphs = self.detail_store.detailed_glyphs_for_entry(
637                    range_idx,
638                    self.entry_buffer[range_idx.to_usize()].glyph_count(),
639                );
640
641                let complex_glyph_range =
642                    range::each_index(ByteIndex(0), ByteIndex(glyphs.len() as isize));
643                Either::Right(complex_glyph_range.map(move |i| {
644                    GlyphInfo::Detail(self, range_idx, i.get() as u16 /* ??? */)
645                }))
646            };
647
648            result.into_iter()
649        })
650    }
651
652    #[inline]
653    pub fn advance_for_byte_range(&self, range: &Range<ByteIndex>, extra_word_spacing: Au) -> Au {
654        if range.begin() == ByteIndex(0) && range.end() == self.len() {
655            self.total_advance + extra_word_spacing * (self.total_word_separators as i32)
656        } else {
657            self.advance_for_byte_range_simple_glyphs(range, extra_word_spacing)
658        }
659    }
660
661    #[inline]
662    pub(crate) fn advance_for_byte_range_simple_glyphs(
663        &self,
664        range: &Range<ByteIndex>,
665        extra_word_spacing: Au,
666    ) -> Au {
667        self.iter_glyphs_for_byte_range(range)
668            .fold(Au::zero(), |advance, glyph| {
669                if glyph.char_is_word_separator() {
670                    advance + glyph.advance() + extra_word_spacing
671                } else {
672                    advance + glyph.advance()
673                }
674            })
675    }
676
677    pub(crate) fn char_is_word_separator(&self, i: ByteIndex) -> bool {
678        assert!(i < self.len());
679        self.entry_buffer[i.to_usize()].char_is_word_separator()
680    }
681}
682
683impl fmt::Debug for GlyphStore {
684    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
685        writeln!(formatter, "GlyphStore:")?;
686        let mut detailed_buffer = self.detail_store.detail_buffer.iter();
687        for entry in self.entry_buffer.iter() {
688            if entry.is_simple() {
689                writeln!(
690                    formatter,
691                    "  simple id={:?} advance={:?}",
692                    entry.id(),
693                    entry.advance()
694                )?;
695                continue;
696            }
697            if entry.is_initial() {
698                continue;
699            }
700            write!(formatter, "  complex...")?;
701            if detailed_buffer.next().is_none() {
702                continue;
703            }
704            writeln!(
705                formatter,
706                "  detailed id={:?} advance={:?}",
707                entry.id(),
708                entry.advance()
709            )?;
710        }
711        Ok(())
712    }
713}
714
715/// A single series of glyphs within a text run.
716#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
717pub struct GlyphRun {
718    /// The glyphs.
719    #[conditional_malloc_size_of]
720    pub glyph_store: Arc<GlyphStore>,
721    /// The byte range of characters in the containing run.
722    pub range: Range<ByteIndex>,
723}
724
725impl GlyphRun {
726    #[inline]
727    pub fn is_single_preserved_newline(&self) -> bool {
728        self.glyph_store.is_single_preserved_newline
729    }
730}