1use 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 servo_base::text::Utf32CodeUnits;
17
18use crate::{GlyphShapingResult, ShapedGlyph, ShapingOptions};
19
20#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
28pub struct GlyphEntry {
29 value: u32,
30}
31
32impl GlyphEntry {
33 fn new(value: u32) -> GlyphEntry {
34 GlyphEntry { value }
35 }
36
37 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
55pub(crate) type GlyphId = u32;
57
58const FLAG_CHAR_IS_WORD_SEPARATOR: u32 = 0x40000000;
61const FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000;
62
63const GLYPH_ADVANCE_MASK: u32 = 0x3FFF0000;
65const GLYPH_ADVANCE_SHIFT: u32 = 16;
66const GLYPH_ID_MASK: u32 = 0x0000FFFF;
67
68fn 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
85impl 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 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, MallocSizeOf)]
129pub struct DetailedGlyphEntry {
130 id: u32,
132 advance: Au,
135 offset: Option<Point2D<Au>>,
137 character_count: Utf32CodeUnits,
140 is_word_separator: bool,
142}
143
144#[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 #[inline]
188 pub fn character_count(self) -> Utf32CodeUnits {
189 match self {
190 GlyphInfo::Simple(..) => Utf32CodeUnits(1),
191 GlyphInfo::Detail(entry) => entry.character_count,
192 }
193 }
194}
195
196#[derive(Clone, MallocSizeOf)]
215pub struct ShapedText {
216 glyphs: Vec<GlyphEntry>,
222
223 detailed_glyphs: Vec<DetailedGlyphEntry>,
226
227 total_advance: Au,
229
230 character_count: Utf32CodeUnits,
232
233 total_word_separators: usize,
236
237 is_rtl: bool,
240}
241
242impl ShapedText {
243 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: Utf32CodeUnits(0),
252 total_word_separators: 0,
253 is_rtl,
254 }
255 }
256
257 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 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 if shaped_glyph_data.len() == 0 {
287 return glyph_store;
288 }
289
290 for mut shaped_glyph in shaped_glyph_data.iter() {
291 let glyph_cluster = shaped_glyph.string_byte_offset;
294
295 if let Some(previous_character_offset) = previous_character_offset &&
296 previous_character_offset == glyph_cluster
297 {
298 glyph_store.add_glyph_for_current_character(&shaped_glyph, options);
299 continue;
300 }
301
302 previous_character_offset = Some(glyph_cluster);
303 let mut characters_skipped = 0;
304 let Some(character) = characters.find_map(|(character_offset, character)| {
305 if glyph_cluster == character_offset {
306 Some(character)
307 } else {
308 characters_skipped += 1;
309 None
310 }
311 }) else {
312 error!("HarfBuzz shaping results extended past character count");
313 return glyph_store;
314 };
315
316 shaped_glyph.adjust_for_character(character, options);
317
318 if shaped_run_is_rtl {
322 glyph_store.add_glyph(character, &shaped_glyph);
323 }
324
325 for _ in 0..characters_skipped {
326 glyph_store.extend_previous_glyph_by_character()
327 }
328
329 if !shaped_run_is_rtl {
333 glyph_store.add_glyph(character, &shaped_glyph);
334 }
335 }
336
337 for (_, _) in characters {
339 glyph_store.extend_previous_glyph_by_character();
340 }
341
342 glyph_store
343 }
344
345 #[inline]
347 pub fn glyph_count(&self) -> usize {
348 self.glyphs.len()
349 }
350
351 pub fn character_count(&self) -> Utf32CodeUnits {
353 self.character_count
354 }
355
356 #[inline]
358 pub(crate) fn add_glyph(&mut self, character: char, glyph: &ShapedGlyph) {
359 if !glyph.can_be_simple_glyph() {
360 self.add_detailed_glyph(glyph, Some(character), Utf32CodeUnits(1));
361 return;
362 }
363
364 let mut simple_glyph_entry = GlyphEntry::simple(glyph.glyph_id, glyph.advance);
365 if character_is_word_separator(character) {
366 self.total_word_separators += 1;
367 simple_glyph_entry.set_char_is_word_separator();
368 }
369
370 self.character_count += Utf32CodeUnits(1);
371 self.total_advance += glyph.advance;
372 self.glyphs.push(simple_glyph_entry)
373 }
374
375 fn add_detailed_glyph(
376 &mut self,
377 shaped_glyph: &ShapedGlyph,
378 character: Option<char>,
379 character_count: Utf32CodeUnits,
380 ) {
381 let is_word_separator = character.is_some_and(character_is_word_separator);
382 if is_word_separator {
383 self.total_word_separators += 1;
384 }
385
386 self.character_count += character_count;
387 self.total_advance += shaped_glyph.advance;
388 self.detailed_glyphs.push(DetailedGlyphEntry {
389 id: shaped_glyph.glyph_id,
390 advance: shaped_glyph.advance,
391 offset: shaped_glyph.offset,
392 character_count,
393 is_word_separator,
394 });
395 self.glyphs
396 .push(GlyphEntry::complex(self.detailed_glyphs.len() - 1));
397 }
398
399 fn extend_previous_glyph_by_character(&mut self) {
400 let detailed_glyph_index = self.ensure_last_glyph_is_detailed();
401 let detailed_glyph = self
402 .detailed_glyphs
403 .get_mut(detailed_glyph_index)
404 .expect("GlyphEntry should have valid index to detailed glyph");
405 detailed_glyph.character_count += Utf32CodeUnits(1);
406 self.character_count += Utf32CodeUnits(1);
407 }
408
409 fn add_glyph_for_current_character(
410 &mut self,
411 shaped_glyph: &ShapedGlyph,
412 options: &ShapingOptions,
413 ) {
414 if options.letter_spacing != Au::zero() {
420 let last_glyph_index = self.ensure_last_glyph_is_detailed();
421 self.detailed_glyphs[last_glyph_index].advance -= options.letter_spacing;
422 }
423
424 self.add_detailed_glyph(shaped_glyph, None, Utf32CodeUnits(0));
427 }
428
429 fn ensure_last_glyph_is_detailed(&mut self) -> usize {
433 let last_glyph = self
434 .glyphs
435 .last_mut()
436 .expect("Should never call this before any glyphs have been added.");
437 if !last_glyph.is_simple() {
438 return last_glyph.detailed_glyph_index();
439 }
440
441 self.detailed_glyphs.push(DetailedGlyphEntry {
442 id: last_glyph.id(),
443 advance: last_glyph.advance(),
444 offset: Default::default(),
445 character_count: Utf32CodeUnits(1),
446 is_word_separator: last_glyph.char_is_word_separator(),
447 });
448
449 let detailed_glyph_index = self.detailed_glyphs.len() - 1;
450 *last_glyph = GlyphEntry::complex(detailed_glyph_index);
451 detailed_glyph_index
452 }
453
454 pub fn glyphs(&self) -> impl DoubleEndedIterator<Item = GlyphInfo<'_>> + use<'_> {
455 self.glyph_slice(0..self.glyphs.len())
456 }
457
458 fn glyph_slice(
459 &self,
460 glyph_range: Range<usize>,
461 ) -> impl DoubleEndedIterator<Item = GlyphInfo<'_>> + use<'_> {
462 self.glyphs[glyph_range].iter().map(|entry| {
463 if entry.is_simple() {
464 GlyphInfo::Simple(entry)
465 } else {
466 GlyphInfo::Detail(&self.detailed_glyphs[entry.detailed_glyph_index()])
467 }
468 })
469 }
470}
471
472impl ShapedGlyph {
473 fn can_be_simple_glyph(&self) -> bool {
474 is_simple_glyph_id(self.glyph_id) &&
475 is_simple_advance(self.advance) &&
476 self.offset
477 .is_none_or(|offset| offset == Default::default())
478 }
479
480 pub(crate) fn adjust_for_character(
483 &mut self,
484 character: char,
485 shaping_options: &ShapingOptions,
486 ) {
487 self.advance += shaping_options.letter_spacing_for_character(character);
488
489 if character == ' ' || character == '\u{a0}' {
494 self.advance += shaping_options.word_spacing;
496 }
497 }
498}
499
500fn character_is_word_separator(character: char) -> bool {
501 let is_word_separator = matches!(
505 character,
506 ' ' |
507 '\u{00A0}' | '\u{1361}' | '\u{10100}' | '\u{10101}' | '\u{1039F}' | '\u{1091F}' );
514 is_word_separator
515}
516
517impl fmt::Debug for ShapedText {
518 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
519 writeln!(formatter, "ShapedText:")?;
520 for entry in self.glyphs.iter() {
521 if entry.is_simple() {
522 writeln!(
523 formatter,
524 " simple id={:?} advance={:?}",
525 entry.id(),
526 entry.advance()
527 )?;
528 continue;
529 } else {
530 let detailed_glyph = &self.detailed_glyphs[entry.detailed_glyph_index()];
531 writeln!(
532 formatter,
533 " detailed id={:?} advance={:?} characters={:?}",
534 detailed_glyph.id, detailed_glyph.advance, detailed_glyph.character_count,
535 )?;
536 }
537 }
538 Ok(())
539 }
540}
541
542#[derive(Clone, Debug, MallocSizeOf)]
543struct GlyphCountAndAdvance {
544 count: usize,
545 advance: Au,
546}
547
548impl GlyphCountAndAdvance {
549 fn new(count: usize, advance: Au) -> Self {
550 Self { count, advance }
551 }
552}
553
554#[derive(Clone, Debug, MallocSizeOf)]
558pub struct TrailingWhiteSpace<Unit> {
559 pub hangable: Unit,
561 pub removable: Unit,
564}
565
566#[derive(Clone, Debug, MallocSizeOf)]
570pub struct ShapedTextSlice {
571 #[conditional_malloc_size_of]
573 shaped_text: Arc<ShapedText>,
574
575 glyph_range: Range<usize>,
577
578 total_advance: Au,
580
581 character_count: Utf32CodeUnits,
583
584 total_word_separators: usize,
587
588 trailing_white_space: TrailingWhiteSpace<GlyphCountAndAdvance>,
591
592 all_white_space: bool,
594}
595
596impl ShapedTextSlice {
597 #[inline]
599 pub fn glyph_count(&self) -> usize {
600 self.glyph_range.len()
601 }
602
603 #[inline]
607 pub fn character_count(&self) -> Utf32CodeUnits {
608 self.character_count
609 }
610
611 #[inline]
613 pub fn total_advance(&self) -> Au {
614 self.total_advance
615 }
616
617 #[inline]
619 pub fn total_word_separators(&self) -> usize {
620 self.total_word_separators
621 }
622
623 pub fn hanging_and_removable_word_separators(&self) -> usize {
625 let discard_glyph_count =
626 self.trailing_white_space.removable.count + self.trailing_white_space.hangable.count;
627 let iterator = if self.shaped_text.is_rtl {
628 self.shaped_text
629 .glyph_slice(self.glyph_range.start..self.glyph_range.start + discard_glyph_count)
630 } else {
631 self.shaped_text
632 .glyph_slice(self.glyph_range.end - discard_glyph_count..self.glyph_range.end)
633 };
634 iterator
635 .map(|glyph| if glyph.char_is_word_separator() { 1 } else { 0 })
636 .sum()
637 }
638
639 pub fn all_removable(&self) -> bool {
641 self.trailing_white_space.removable.count >= self.glyph_count()
642 }
643
644 pub fn has_non_hangable_non_removable_content(&self) -> bool {
646 self.trailing_white_space.removable.count + self.trailing_white_space.hangable.count <
647 self.glyph_count()
648 }
649
650 pub fn removable_advance(&self) -> Au {
652 self.trailing_white_space.removable.advance
653 }
654
655 pub fn hangable_advance(&self) -> Au {
657 self.trailing_white_space.hangable.advance
658 }
659
660 pub fn all_white_space(&self) -> bool {
662 self.all_white_space
663 }
664
665 pub fn glyphs(&self) -> impl DoubleEndedIterator<Item = GlyphInfo<'_>> + use<'_> {
667 self.shaped_text.glyph_slice(self.glyph_range.clone())
668 }
669
670 fn split_off_count(&mut self, trimmed_glyph_count: usize) -> Arc<Self> {
671 let glyph_range = if self.shaped_text.is_rtl {
672 self.glyph_range.start + trimmed_glyph_count..self.glyph_range.end
673 } else {
674 self.glyph_range.start..self.glyph_range.end - trimmed_glyph_count
675 };
676
677 let split_glyph_range = if self.shaped_text.is_rtl {
678 self.glyph_range.start..glyph_range.start
679 } else {
680 glyph_range.end..self.glyph_range.end
681 };
682
683 let mut trimmed_characters = Utf32CodeUnits(0);
684 let mut trimmed_word_separators = 0;
685 let mut trimmed_advance = Au::zero();
686 let iterator = self.shaped_text.glyph_slice(split_glyph_range.clone());
687 for glyph in iterator {
688 trimmed_characters += glyph.character_count();
689 trimmed_advance += glyph.advance();
690 if glyph.char_is_word_separator() {
691 trimmed_word_separators += 1;
692 }
693 }
694
695 self.glyph_range = glyph_range;
696 self.character_count -= trimmed_characters;
697 self.total_word_separators -= trimmed_word_separators;
698 self.total_advance -= trimmed_advance;
699
700 let GlyphCountAndAdvance {
701 count: removable_glyphs,
702 advance: removable_advance,
703 } = self.trailing_white_space.removable;
704
705 let removable_glyphs_to_trim = trimmed_glyph_count.min(removable_glyphs);
706 let removable_advance_to_trim = trimmed_advance.min(removable_advance);
707 let hangable_glyphs_to_trim = trimmed_glyph_count - removable_glyphs_to_trim;
708 let hangable_advance_to_trim =
709 (trimmed_advance - removable_advance_to_trim).min(self.hangable_advance());
710
711 self.trailing_white_space.removable.count -= removable_glyphs_to_trim;
712 self.trailing_white_space.removable.advance -= removable_advance_to_trim;
713 self.trailing_white_space.hangable.count -= hangable_glyphs_to_trim;
714 self.trailing_white_space.hangable.advance -= hangable_advance_to_trim;
715
716 Arc::new(Self {
717 shaped_text: self.shaped_text.clone(),
718 glyph_range: split_glyph_range,
719 total_advance: trimmed_advance,
720 character_count: trimmed_characters,
721 total_word_separators: trimmed_word_separators,
722 trailing_white_space: TrailingWhiteSpace {
723 hangable: GlyphCountAndAdvance::new(
724 hangable_glyphs_to_trim,
725 hangable_advance_to_trim,
726 ),
727 removable: GlyphCountAndAdvance::new(
728 removable_glyphs_to_trim,
729 removable_advance_to_trim,
730 ),
731 },
732 all_white_space: true,
736 })
737 }
738
739 pub fn without_removable_white_space(&self) -> Arc<Self> {
742 let mut new = self.clone();
743 new.split_off_count(self.trailing_white_space.removable.count);
744 Arc::new(new)
745 }
746}
747
748pub struct ShapedTextSlicer {
750 current_glyph_offset: usize,
751 current_character_offset: Utf32CodeUnits,
752 shaped_text: Arc<ShapedText>,
753}
754
755impl ShapedTextSlicer {
756 pub fn new(shaped_text: Arc<ShapedText>) -> Self {
757 let current_glyph_offset = if shaped_text.is_rtl {
758 shaped_text.glyph_count()
759 } else {
760 0
761 };
762
763 Self {
764 current_glyph_offset,
765 current_character_offset: Utf32CodeUnits(0),
766 shaped_text,
767 }
768 }
769
770 pub fn slice_until_character_offset(
775 &mut self,
776 desired_character_offset: Utf32CodeUnits,
777 trailing_white_space: TrailingWhiteSpace<Utf32CodeUnits>,
778 all_white_space: bool,
779 ) -> Option<Arc<ShapedTextSlice>> {
780 let mut glyph_count = 0;
781 let mut total_word_separators = 0;
782 let mut total_advance = Au::zero();
783 let original_character_offset = self.current_character_offset;
784
785 if self.current_character_offset >= desired_character_offset {
786 return None;
787 }
788
789 let starting_removable_offset = desired_character_offset - trailing_white_space.removable;
790 let starting_hangable_offset = starting_removable_offset - trailing_white_space.hangable;
791 let mut hangable_glyph_count = 0;
792 let mut hangable_advance = Au::zero();
793 let mut removable_glyph_count = 0;
794 let mut removable_advance = Au::zero();
795
796 let iterator = if self.shaped_text.is_rtl {
801 Either::Left(
802 self.shaped_text
803 .glyph_slice(0..self.current_glyph_offset)
804 .rev(),
805 )
806 } else {
807 Either::Right(
808 self.shaped_text
809 .glyph_slice(self.current_glyph_offset..self.shaped_text.glyph_count()),
810 )
811 };
812
813 for glyph in iterator {
814 if self.current_character_offset >= desired_character_offset &&
817 glyph.character_count().0 > 0
818 {
819 break;
820 }
821
822 if self.current_character_offset >= starting_removable_offset {
823 removable_advance += glyph.advance();
824 removable_glyph_count += 1;
825 } else if self.current_character_offset >= starting_hangable_offset {
826 hangable_advance += glyph.advance();
827 hangable_glyph_count += 1;
828 }
829
830 glyph_count += 1;
831 self.current_character_offset += glyph.character_count();
832 total_advance += glyph.advance();
833 if glyph.char_is_word_separator() {
834 total_word_separators += 1;
835 }
836 }
837
838 let (new_glyph_offset, glyph_range) = if self.shaped_text.is_rtl {
839 assert!(self.current_glyph_offset >= glyph_count);
840 let new_glyph_offset = self.current_glyph_offset - glyph_count;
841 (
842 new_glyph_offset,
843 new_glyph_offset..self.current_glyph_offset,
844 )
845 } else {
846 let new_glyph_offset = self.current_glyph_offset + glyph_count;
847 (
848 new_glyph_offset,
849 self.current_glyph_offset..new_glyph_offset,
850 )
851 };
852
853 if glyph_count == 0 {
854 return None;
855 }
856
857 self.current_glyph_offset = new_glyph_offset;
858 Some(Arc::new(ShapedTextSlice {
859 shaped_text: self.shaped_text.clone(),
860 glyph_range,
861 total_advance,
862 character_count: self.current_character_offset - original_character_offset,
863 total_word_separators,
864 trailing_white_space: TrailingWhiteSpace {
865 hangable: GlyphCountAndAdvance::new(hangable_glyph_count, hangable_advance),
866 removable: GlyphCountAndAdvance::new(removable_glyph_count, removable_advance),
867 },
868 all_white_space,
869 }))
870 }
871}