1use std::{
2 cmp, fmt,
3 hash::{BuildHasher, Hash},
4 mem, ops,
5};
6
7use zng_app::widget::info::InlineSegmentInfo;
8use zng_ext_image::{IMAGES, ImageDataFormat, 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#[derive(Clone, Copy, PartialEq, Debug)]
24#[non_exhaustive]
25pub enum GlyphLoadingError {
26 NoSuchGlyph,
28 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#[derive(Debug, Clone)]
44#[non_exhaustive]
45pub struct TextShapingArgs {
46 pub letter_spacing: Px,
48
49 pub word_spacing: Px,
51
52 pub line_height: Px,
58
59 pub line_spacing: Px,
61
62 pub paragraph_spacing: Px,
64
65 pub paragraph_indent: (Px, bool),
69
70 pub lang: Lang,
72
73 pub direction: LayoutDirection,
75
76 pub ignore_ligatures: bool,
78
79 pub disable_kerning: bool,
81
82 pub tab_x_advance: Px,
84
85 pub inline_constraints: Option<InlineConstraintsMeasure>,
87
88 pub font_features: RFontFeatures,
90
91 pub max_width: Px,
95
96 pub paragraph_break: ParagraphBreak,
98
99 pub line_break: LineBreak,
101
102 pub word_break: WordBreak,
106
107 pub hyphens: Hyphens,
109
110 pub hyphen_char: Txt,
112
113 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#[derive(Debug, Clone, Default)]
145#[non_exhaustive]
146pub struct TextReshapingArgs {
147 pub constraints: PxConstraints2d,
149 pub inline_constraints: Option<InlineConstraintsLayout>,
151 pub align: Align,
153 pub overflow_align: Align,
155 pub direction: LayoutDirection,
157
158 pub line_height: Px,
160 pub line_spacing: Px,
162 pub paragraph_spacing: Px,
164 pub paragraph_indent: (Px, bool),
168 pub paragraph_break: ParagraphBreak,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq)]
174struct LineRange {
175 end: usize,
178 width: f32,
180 x_offset: f32,
182 directions: LayoutDirections,
183}
184
185#[derive(Clone)]
187struct FontRange {
188 font: Font,
189 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 pub end: usize,
211 pub x: f32,
213 pub advance: f32,
215}
216
217#[derive(Debug, Default, Clone, PartialEq)]
219struct GlyphSegmentVec(Vec<GlyphSegment>);
220impl GlyphSegmentVec {
221 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 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#[derive(Debug, Default, Clone, PartialEq)]
245struct LineRangeVec(Vec<LineRange>);
246impl LineRangeVec {
247 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 fn width(&self, index: usize) -> f32 {
256 self.0[index].width
257 }
258
259 fn x_offset(&self, index: usize) -> f32 {
261 self.0[index].x_offset
262 }
263
264 fn iter_segs(&self) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
266 self.iter_segs_skip(0)
267 }
268
269 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 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#[derive(Debug, Default, Clone, PartialEq)]
300struct FontRangeVec(Vec<FontRange>);
301impl FontRangeVec {
302 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 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 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#[derive(Debug, Clone, PartialEq)]
354pub struct ShapedText {
355 glyphs: Vec<GlyphInstance>,
357 clusters: Vec<u32>,
359 segments: GlyphSegmentVec,
361 lines: LineRangeVec,
362 fonts: FontRangeVec,
363 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,
373 orig_line_spacing: Px,
374 orig_first_line: PxSize,
375 orig_last_line: PxSize,
376
377 baseline: Px,
379 overline: Px,
380 strikethrough: Px,
381 underline: Px,
382 underline_descent: Px,
383
384 mid_offset: f32,
386 align_size: PxSize,
387 align: Align,
388 justify: Justify, justified: Vec<f32>, overflow_align: Align,
391 direction: LayoutDirection,
392
393 is_inlined: bool,
395 first_wrapped: bool,
396 first_line: PxRect,
397 mid_clear: Px,
398 mid_size: PxSize,
399 last_line: PxRect,
400
401 has_colored_glyphs: bool,
402}
403
404pub enum ShapedColoredGlyphs<'a> {
406 Normal(&'a [GlyphInstance]),
408 Colored {
410 point: euclid::Point2D<f32, Px>,
412 base_glyph: GlyphIndex,
416
417 glyphs: super::ColorGlyph<'a>,
419 },
420}
421
422pub enum ShapedImageGlyphs<'a> {
424 Normal(&'a [GlyphInstance]),
426 Image {
428 rect: euclid::Rect<f32, Px>,
432 base_glyph: GlyphIndex,
436 img: &'a ImageVar,
438 },
439}
440
441impl ShapedText {
442 pub fn new(font: &Font) -> Self {
444 font.shape_text(&SegmentedText::new("", LayoutDirection::LTR), &TextShapingArgs::default())
445 }
446
447 pub fn glyphs(&self) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
456 self.fonts.iter_glyphs().map(move |(f, r)| (f, &self.glyphs[r.iter()]))
457 }
458
459 pub fn glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
465 self.glyphs_slice_impl(IndexRange::from_bounds(range))
466 }
467 fn glyphs_slice_impl(&self, range: IndexRange) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
468 self.fonts.iter_glyphs_clip(range).map(move |(f, r)| (f, &self.glyphs[r.iter()]))
469 }
470
471 pub fn has_colored_glyphs(&self) -> bool {
473 self.has_colored_glyphs
474 }
475
476 pub fn has_images(&self) -> bool {
478 !self.images.is_empty()
479 }
480
481 pub fn colored_glyphs(&self) -> impl Iterator<Item = (&Font, ShapedColoredGlyphs<'_>)> {
483 ColoredGlyphsIter {
484 glyphs: self.glyphs(),
485 maybe_colored: None,
486 }
487 }
488
489 pub fn colored_glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, ShapedColoredGlyphs<'_>)> {
491 ColoredGlyphsIter {
492 glyphs: self.glyphs_slice_impl(IndexRange::from_bounds(range)),
493 maybe_colored: None,
494 }
495 }
496
497 pub fn image_glyphs(&self) -> impl Iterator<Item = (&Font, ShapedImageGlyphs<'_>)> {
499 ImageGlyphsIter {
500 glyphs: self.glyphs(),
501 glyphs_i: 0,
502 images: &self.images,
503 maybe_img: None,
504 }
505 }
506
507 pub fn image_glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, ShapedImageGlyphs<'_>)> {
509 let range = IndexRange::from_bounds(range);
510 ImageGlyphsIter {
511 glyphs_i: range.start() as _,
512 glyphs: self.glyphs_slice_impl(range),
513 images: &self.images,
514 maybe_img: None,
515 }
516 }
517
518 fn glyphs_range(&self, range: IndexRange) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
520 self.fonts.iter_glyphs_clip(range).map(|(f, r)| (f, &self.glyphs[r.iter()]))
521 }
522
523 fn clusters_range(&self, range: IndexRange) -> &[u32] {
526 &self.clusters[range.iter()]
527 }
528
529 fn seg_glyphs_with_x_advance(
530 &self,
531 seg_idx: usize,
532 glyphs_range: IndexRange,
533 ) -> impl Iterator<Item = (&Font, impl Iterator<Item = (GlyphInstance, f32)> + '_)> + '_ {
534 let mut gi = glyphs_range.start();
535 let seg_x = if gi < self.glyphs.len() { self.glyphs[gi].point.x } else { 0.0 };
536 let seg_advance = self.segments.0[seg_idx].advance;
537 self.glyphs_range(glyphs_range).map(move |(font, glyphs)| {
538 let g_adv = glyphs.iter().map(move |g| {
539 gi += 1;
540
541 let adv = if gi == glyphs_range.end() {
542 (seg_x + seg_advance) - g.point.x
543 } else {
544 self.glyphs[gi].point.x - g.point.x
545 };
546 (*g, adv)
547 });
548
549 (font, g_adv)
550 })
551 }
552
553 fn seg_cluster_glyphs_with_x_advance(
554 &self,
555 seg_idx: usize,
556 glyphs_range: IndexRange,
557 ) -> impl Iterator<Item = (&Font, impl Iterator<Item = (u32, &[GlyphInstance], f32)>)> {
558 let mut gi = glyphs_range.start();
559 let seg_x = if gi < self.glyphs.len() { self.glyphs[gi].point.x } else { 0.0 };
560 let seg_advance = self.segments.0[seg_idx].advance;
561 let seg_clusters = self.clusters_range(glyphs_range);
562 let mut cluster_i = 0;
563
564 self.glyphs_range(glyphs_range).map(move |(font, glyphs)| {
565 let clusters = &seg_clusters[cluster_i..cluster_i + glyphs.len()];
566 cluster_i += glyphs.len();
567
568 struct Iter<'a> {
569 clusters: &'a [u32],
570 glyphs: &'a [GlyphInstance],
571 }
572 impl<'a> Iterator for Iter<'a> {
573 type Item = (u32, &'a [GlyphInstance]);
574
575 fn next(&mut self) -> Option<Self::Item> {
576 if let Some(c) = self.clusters.first() {
577 let end = self.clusters.iter().rposition(|rc| rc == c).unwrap();
578 let glyphs = &self.glyphs[..=end];
579 self.clusters = &self.clusters[end + 1..];
580 self.glyphs = &self.glyphs[end + 1..];
581 Some((*c, glyphs))
582 } else {
583 None
584 }
585 }
586 }
587
588 let g_adv = Iter { clusters, glyphs }.map(move |(c, gs)| {
589 gi += gs.len();
590
591 let adv = if gi == glyphs_range.end() {
592 (seg_x + seg_advance) - gs[0].point.x
593 } else {
594 self.glyphs[gi].point.x - gs[0].point.x
595 };
596 (c, gs, adv)
597 });
598
599 (font, g_adv)
600 })
601 }
602
603 pub fn size(&self) -> PxSize {
606 let first_width = self.first_line.origin.x.abs() + self.first_line.size.width;
607 let last_width = self.last_line.origin.x.abs() + self.last_line.size.width;
608 self.mid_size()
609 .max(PxSize::new(first_width.max(last_width), self.last_line.max_y()))
610 }
611
612 pub fn block_size(&self) -> PxSize {
614 if self.lines.0.is_empty() {
615 PxSize::zero()
616 } else if self.lines.0.len() == 1 {
617 self.first_line.size
618 } else {
619 let mut s = PxSize::new(
620 self.first_line.size.width.max(self.last_line.size.width),
621 self.first_line.size.height + self.line_spacing + self.last_line.size.height,
622 );
623 if self.lines.0.len() > 2 {
624 s.width = s.width.max(self.mid_size.width);
625 s.height += self.mid_size.height + self.line_spacing;
626 }
627 s
628 }
629 }
630
631 pub fn overflow_line(&self, max_height: Px) -> Option<ShapedLine<'_>> {
634 let mut y = self.first_line.max_y();
635 if y > max_height {
636 self.line(0)
637 } else if self.lines.0.len() > 1 {
638 let mid_lines = self.lines.0.len() - 2;
639 for i in 0..=mid_lines {
640 y += self.line_spacing;
641 y += self.line_height;
642 if y > max_height {
643 return self.line(i + 1);
644 }
645 }
646
647 if self.last_line.max_y() > max_height {
648 self.line(self.lines.0.len() - 1)
649 } else {
650 None
651 }
652 } else {
653 None
654 }
655 }
656
657 fn update_mid_size(&mut self) {
658 self.mid_size = if self.lines.0.len() <= 2 {
659 PxSize::zero()
660 } else {
661 let mid_lines = &self.lines.0[1..self.lines.0.len() - 1];
662 PxSize::new(
663 Px(mid_lines.iter().map(|l| l.width).max_by(f32_cmp).unwrap_or_default().ceil() as i32),
664 Px(mid_lines.len() as i32) * self.line_height + Px((mid_lines.len() - 1) as i32) * self.line_spacing,
665 )
666 };
667 }
668
669 fn update_first_last_lines(&mut self) {
670 if self.lines.0.is_empty() {
671 self.first_line = PxRect::zero();
672 self.last_line = PxRect::zero();
673 self.align_size = PxSize::zero();
674 } else {
675 self.first_line = PxRect::from_size(PxSize::new(Px(self.lines.first_mut().width.ceil() as i32), self.line_height));
676
677 if self.lines.0.len() > 1 {
678 self.last_line.size = PxSize::new(Px(self.lines.last().width.ceil() as i32), self.line_height);
679 self.last_line.origin = PxPoint::new(Px(0), self.first_line.max_y() + self.line_spacing);
680 if self.lines.0.len() > 2 {
681 self.last_line.origin.y += self.mid_size.height + self.line_spacing;
682 }
683 } else {
684 self.last_line = self.first_line;
685 }
686 self.align_size = self.block_size();
687 }
688 }
689
690 pub fn mid_size(&self) -> PxSize {
692 self.mid_size
693 }
694
695 pub fn is_inlined(&self) -> bool {
702 self.is_inlined
703 }
704
705 pub fn align(&self) -> Align {
709 self.align
710 }
711
712 pub fn justify_mode(&self) -> Option<Justify> {
718 match self.justify {
719 Justify::Auto => None,
720 m => {
721 debug_assert!(self.align.is_fill_x());
722 Some(m)
723 }
724 }
725 }
726
727 pub fn overflow_align(&self) -> Align {
733 self.overflow_align
734 }
735
736 pub fn align_size(&self) -> PxSize {
740 self.align_size
741 }
742
743 pub fn direction(&self) -> LayoutDirection {
748 self.direction
749 }
750
751 pub fn mid_clear(&self) -> Px {
754 self.mid_clear
755 }
756
757 #[expect(clippy::too_many_arguments)]
761 #[deprecated = "use `reshape_lines2`"]
762 pub fn reshape_lines(
763 &mut self,
764 constraints: PxConstraints2d,
765 inline_constraints: Option<InlineConstraintsLayout>,
766 align: Align,
767 overflow_align: Align,
768 line_height: Px,
769 line_spacing: Px,
770 direction: LayoutDirection,
771 ) {
772 self.reshape_lines2(&TextReshapingArgs {
773 constraints,
774 inline_constraints,
775 align,
776 overflow_align,
777 direction,
778 line_height,
779 line_spacing,
780 paragraph_spacing: Px(0),
781 paragraph_indent: (Px(0), false),
782 paragraph_break: ParagraphBreak::None,
783 });
784 }
785
786 pub fn reshape_lines2(&mut self, args: &TextReshapingArgs) {
798 self.clear_justify_impl(args.align.is_fill_x());
799 self.reshape_line_height_and_spacing(args.line_height, args.line_spacing);
800
801 let is_inlined = args.inline_constraints.is_some();
802
803 let align_x = args.align.x(args.direction);
804 let align_y = if is_inlined { 0.fct() } else { args.align.y() };
805 let overflow_align_x = args.overflow_align.x(args.direction);
806 let overflow_align_y = if is_inlined { 0.fct() } else { args.overflow_align.y() };
807
808 let (first, mid, last, first_segs, last_segs) = if let Some(l) = &args.inline_constraints {
809 (l.first, l.mid_clear, l.last, &*l.first_segs, &*l.last_segs)
810 } else {
811 let block_size = self.block_size();
813 let align_size = args.constraints.fill_size_or(block_size);
814
815 let mut first = PxRect::from_size(self.line(0).map(|l| l.rect().size).unwrap_or_default());
816 let mut last = PxRect::from_size(
817 self.line(self.lines_len().saturating_sub(1))
818 .map(|l| l.rect().size)
819 .unwrap_or_default(),
820 );
821 last.origin.y = block_size.height - last.size.height;
822
823 match first.size.width.cmp(&align_size.width) {
824 cmp::Ordering::Less => first.origin.x = (align_size.width - first.size.width) * align_x,
825 cmp::Ordering::Equal => {}
826 cmp::Ordering::Greater => first.origin.x = (align_size.width - first.size.width) * overflow_align_x,
827 }
828 match last.size.width.cmp(&align_size.width) {
829 cmp::Ordering::Less => last.origin.x = (align_size.width - last.size.width) * align_x,
830 cmp::Ordering::Equal => {}
831 cmp::Ordering::Greater => last.origin.x = (align_size.width - last.size.width) * overflow_align_x,
832 }
833
834 match block_size.height.cmp(&align_size.height) {
835 cmp::Ordering::Less => {
836 let align_y = (align_size.height - block_size.height) * align_y;
837 first.origin.y += align_y;
838 last.origin.y += align_y;
839 }
840 cmp::Ordering::Equal => {}
841 cmp::Ordering::Greater => {
842 let align_y = (align_size.height - block_size.height) * overflow_align_y;
843 first.origin.y += align_y;
844 last.origin.y += align_y;
845 }
846 }
847
848 static EMPTY: Vec<InlineSegmentPos> = vec![];
849 (first, Px(0), last, &EMPTY, &EMPTY)
850 };
851
852 if !self.lines.0.is_empty() {
853 if self.first_line != first {
854 let first_offset = (first.origin - self.first_line.origin).cast::<f32>().cast_unit();
855
856 let first_range = self.lines.segs(0);
857 let first_glyphs = self.segments.glyphs_range(first_range);
858
859 for g in &mut self.glyphs[first_glyphs.iter()] {
860 g.point += first_offset;
861 }
862
863 let first_line = self.lines.first_mut();
864 first_line.x_offset = first.origin.x.0 as f32;
865 }
869 if !first_segs.is_empty() {
870 let first_range = self.lines.segs(0);
872 if first_range.len() == first_segs.len() {
873 for i in first_range.iter() {
874 let seg_offset = first_segs[i].x - self.segments.0[i].x;
875 let glyphs = self.segments.glyphs(i);
876 for g in &mut self.glyphs[glyphs.iter()] {
877 g.point.x += seg_offset;
878 }
879 self.segments.0[i].x = first_segs[i].x;
880 }
881 } else {
882 #[cfg(debug_assertions)]
883 {
884 tracing::error!("expected {} segments in `first_segs`, was {}", first_range.len(), first_segs.len());
885 }
886 }
887 }
888 }
889
890 if self.lines.0.len() > 1 {
891 if self.last_line != last {
892 let last_offset = (last.origin - self.last_line.origin).cast::<f32>().cast_unit();
895
896 let last_range = self.lines.segs(self.lines.0.len() - 1);
897 let last_glyphs = self.segments.glyphs_range(last_range);
898
899 for g in &mut self.glyphs[last_glyphs.iter()] {
900 g.point += last_offset;
901 }
902
903 let last_line = self.lines.last_mut();
904 last_line.x_offset = last.origin.x.0 as f32;
905 }
908 if !last_segs.is_empty() {
909 let last_range = self.lines.segs(self.lines.0.len() - 1);
911
912 if last_range.len() == last_segs.len() {
913 for i in last_range.iter() {
914 let li = i - last_range.start();
915
916 let seg_offset = last_segs[li].x - self.segments.0[i].x;
917 let glyphs = self.segments.glyphs(i);
918 for g in &mut self.glyphs[glyphs.iter()] {
919 g.point.x += seg_offset;
920 }
921 self.segments.0[i].x = last_segs[li].x;
922 }
923 } else {
924 #[cfg(debug_assertions)]
925 {
926 tracing::error!("expected {} segments in `last_segs`, was {}", last_range.len(), last_segs.len());
927 }
928 }
929 }
930 }
931
932 self.first_line = first;
933 self.last_line = last;
934
935 let block_size = self.block_size();
936 let align_size = args.constraints.fill_size_or(block_size);
937
938 if self.lines.0.len() > 2 {
939 let mid_offset = euclid::vec2::<f32, Px>(
942 0.0,
943 match block_size.height.cmp(&align_size.height) {
944 cmp::Ordering::Less => (align_size.height - block_size.height).0 as f32 * align_y + mid.0 as f32,
945 cmp::Ordering::Equal => mid.0 as f32,
946 cmp::Ordering::Greater => (align_size.height - block_size.height).0 as f32 * overflow_align_y + mid.0 as f32,
947 },
948 );
949 let y_transform = mid_offset.y - self.mid_offset;
950 let align_width = align_size.width.0 as f32;
951
952 let skip_last = self.lines.0.len() - 2;
953 let mut line_start = self.lines.0[0].end;
954 for line in &mut self.lines.0[1..=skip_last] {
955 let x_offset = if line.width < align_width {
956 (align_width - line.width) * align_x
957 } else {
958 (align_width - line.width) * overflow_align_x
959 };
960 let x_transform = x_offset - line.x_offset;
961
962 let glyphs = self.segments.glyphs_range(IndexRange(line_start, line.end));
963 for g in &mut self.glyphs[glyphs.iter()] {
964 g.point.x += x_transform;
965 g.point.y += y_transform;
966 }
967 line.x_offset = x_offset;
968
969 line_start = line.end;
970 }
971
972 let y_transform_px = Px(y_transform as i32);
973 self.underline -= y_transform_px;
974 self.baseline -= y_transform_px;
975 self.overline -= y_transform_px;
976 self.strikethrough -= y_transform_px;
977 self.underline_descent -= y_transform_px;
978 self.mid_offset = mid_offset.y;
979 }
980
981 let baseline_offset =
983 if self.align.is_baseline() { -self.baseline } else { Px(0) } + if args.align.is_baseline() { self.baseline } else { Px(0) };
984 if baseline_offset != Px(0) {
985 let baseline_offset = baseline_offset.0 as f32;
986 for g in &mut self.glyphs {
987 g.point.y += baseline_offset;
988 }
989 }
990
991 self.align_size = align_size;
992 self.align = args.align;
993 self.direction = args.direction;
994 self.is_inlined = is_inlined;
995
996 self.debug_assert_ranges();
997 }
998 fn reshape_line_height_and_spacing(&mut self, line_height: Px, line_spacing: Px) {
999 let mut update_height = false;
1000
1001 if self.line_height != line_height {
1002 let offset_y = (line_height - self.line_height).0 as f32;
1003 let mut offset = 0.0;
1004 let center = offset_y / 2.0;
1005
1006 self.first_line.origin.y += Px(center as i32);
1007
1008 for (_, r) in self.lines.iter_segs() {
1009 let r = self.segments.glyphs_range(r);
1010 for g in &mut self.glyphs[r.iter()] {
1011 g.point.y += offset + center;
1012 }
1013
1014 offset += offset_y;
1015 }
1016
1017 self.line_height = line_height;
1018 update_height = true;
1019 }
1020
1021 if self.line_spacing != line_spacing {
1022 if self.lines.is_multi() {
1023 let offset_y = (line_spacing - self.line_spacing).0 as f32;
1024 let mut offset = offset_y;
1025
1026 for (_, r) in self.lines.iter_segs_skip(1) {
1027 let r = self.segments.glyphs_range(r);
1028
1029 for g in &mut self.glyphs[r.iter()] {
1030 g.point.y += offset;
1031 }
1032
1033 offset += offset_y;
1034 }
1035 offset -= offset_y;
1036
1037 self.last_line.origin.y += Px(offset as i32);
1038
1039 update_height = true;
1040 }
1041 self.line_spacing = line_spacing;
1042 }
1043
1044 if update_height {
1045 self.update_mid_size();
1046
1047 if !self.is_inlined {
1048 self.update_first_last_lines();
1049 }
1050 }
1051 }
1052
1053 pub fn clear_reshape(&mut self) {
1055 self.reshape_lines2(&TextReshapingArgs::default());
1056 }
1057
1058 fn justify_lines_range(&self) -> ops::Range<usize> {
1059 let mut range = 0..self.lines_len();
1060
1061 if !self.is_inlined {
1062 range.end = range.end.saturating_sub(1);
1064 }
1065 range
1068 }
1069
1070 pub fn reshape_lines_justify(&mut self, mode: Justify, lang: &Lang) {
1075 self.clear_justify_impl(true);
1076
1077 if !self.align.is_fill_x() {
1078 return;
1079 }
1080
1081 let mode = mode.resolve(lang);
1082
1083 let range = self.justify_lines_range();
1084
1085 let fill_width = self.align_size.width.0 as f32;
1086 let last_li = range.end.saturating_sub(1);
1087
1088 for li in range.clone() {
1089 let mut count;
1090 let mut space;
1091 let mut line_seg_range;
1092 let mut offset = 0.0;
1093 let mut last_is_space = false;
1094
1095 let mut fill_width = fill_width;
1096 if self.is_inlined {
1097 if li == 0 {
1099 fill_width = self.first_line.width().0 as f32;
1100 } else if li == last_li {
1101 fill_width = self.last_line.width().0 as f32;
1102 }
1103 }
1104
1105 {
1106 let line = self.line(li).unwrap();
1108
1109 count = match mode {
1111 Justify::InterWord => line.segs().filter(|s| s.kind().is_space()).count(),
1112 Justify::InterLetter => line
1113 .segs()
1114 .map(|s| {
1115 if s.kind().is_space() {
1116 s.clusters_count().saturating_sub(1).max(1)
1117 } else if s.kind().is_word() {
1118 s.clusters_count().saturating_sub(1)
1119 } else {
1120 0
1121 }
1122 })
1123 .sum(),
1124 Justify::Auto => unreachable!(),
1125 };
1126
1127 space = fill_width - self.lines.0[li].width;
1129
1130 line_seg_range = 0..line.segs_len();
1131
1132 let mut first_is_space = false;
1134
1135 if let Some(s) = line.seg(0)
1136 && s.kind().is_space()
1137 && (!self.is_inlined || li > 0 || self.first_line.origin.x == Px(0))
1138 {
1139 first_is_space = true;
1141 count -= 1;
1142 space += s.advance();
1143 }
1144 if let Some(s) = line.seg(line.segs_len().saturating_sub(1))
1145 && s.kind().is_space()
1146 && (!self.is_inlined || li < range.end - 1 || about_eq(self.first_line.size.width.0 as f32, fill_width, 1.0))
1147 {
1148 last_is_space = true;
1150 count -= 1;
1151 space += s.advance();
1152 }
1153 if first_is_space {
1154 line_seg_range.start += 1;
1155 let gsi = self.line(li).unwrap().seg_range.start();
1156 let adv = mem::take(&mut self.segments.0[gsi].advance);
1157 offset -= adv;
1158 self.justified.push(adv);
1159 }
1160 if last_is_space {
1161 line_seg_range.end = line_seg_range.end.saturating_sub(1);
1162 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1163 let adv = mem::take(&mut self.segments.0[gsi].advance);
1164 self.justified.push(adv);
1165 }
1166 if line_seg_range.start > line_seg_range.end {
1167 line_seg_range = 0..0;
1168 }
1169 }
1170 let justify_advance = space / count as f32;
1171 self.justified.push(justify_advance);
1172
1173 for si in line_seg_range {
1174 let is_space;
1175 let glyphs_range;
1176 let gsi;
1177 {
1178 let line = self.line(li).unwrap();
1179 let seg = line.seg(si).unwrap();
1180
1181 is_space = seg.kind().is_space();
1182 glyphs_range = seg.glyphs_range();
1183 gsi = line.seg_range.start() + si;
1184 }
1185
1186 let mut cluster = if self.clusters.is_empty() {
1187 0
1188 } else {
1189 self.clusters[glyphs_range.start()]
1190 };
1191 for gi in glyphs_range {
1192 self.glyphs[gi].point.x += offset;
1193
1194 if matches!(mode, Justify::InterLetter) && self.clusters[gi] != cluster {
1195 cluster = self.clusters[gi];
1196 offset += justify_advance;
1197 self.segments.0[gsi].advance += justify_advance;
1198 }
1199 }
1200
1201 let seg = &mut self.segments.0[gsi];
1202 seg.x += offset;
1203 if is_space {
1204 offset += justify_advance;
1205 seg.advance += justify_advance;
1206 }
1207 }
1208 if last_is_space {
1209 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1210 let seg = &mut self.segments.0[gsi];
1211 debug_assert_eq!(seg.advance, 0.0);
1212 seg.x += offset;
1213 }
1214 self.justified.shrink_to_fit();
1215 }
1216
1217 self.justify = mode;
1218 }
1219
1220 pub fn clear_justify(&mut self) {
1224 self.clear_justify_impl(false)
1225 }
1226 fn clear_justify_impl(&mut self, keep_alloc: bool) {
1227 if self.justify_mode().is_none() {
1228 return;
1229 }
1230
1231 let range = self.justify_lines_range();
1232 debug_assert!(range.len() <= self.justified.len());
1233
1234 let mut justified_alloc = mem::take(&mut self.justified);
1235
1236 let mut justified = justified_alloc.drain(..);
1237 for li in range {
1238 let mut line_seg_range;
1239 let mut last_is_space = false;
1240
1241 let mut offset = 0.0;
1242
1243 {
1244 let line = self.line(li).unwrap();
1245
1246 line_seg_range = 0..line.segs_len();
1247
1248 let mut first_is_space = false;
1250
1251 if let Some(s) = line.seg(0) {
1252 first_is_space = s.kind().is_space();
1253 }
1254 if let Some(s) = line.seg(line.segs_len().saturating_sub(1)) {
1255 last_is_space = s.kind().is_space();
1256 }
1257 if first_is_space {
1258 line_seg_range.start += 1;
1259 let gsi = self.line(li).unwrap().seg_range.start();
1260
1261 let adv = justified.next().unwrap();
1262 self.segments.0[gsi].advance = adv;
1263 offset -= adv;
1264 }
1265 if last_is_space {
1266 line_seg_range.end = line_seg_range.end.saturating_sub(1);
1267 let adv = justified.next().unwrap();
1268 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1269 self.segments.0[gsi].advance = adv;
1270 }
1271 if line_seg_range.start > line_seg_range.end {
1272 line_seg_range = 0..0;
1273 }
1274 }
1275
1276 let justify_advance = justified.next().unwrap();
1277
1278 for si in line_seg_range {
1279 let is_space;
1280 let glyphs_range;
1281 let gsi;
1282 {
1283 let line = self.line(li).unwrap();
1284 let seg = line.seg(si).unwrap();
1285
1286 is_space = seg.kind().is_space();
1287 glyphs_range = seg.glyphs_range();
1288 gsi = line.seg_range.start() + si;
1289 }
1290
1291 let mut cluster = if self.clusters.is_empty() {
1292 0
1293 } else {
1294 self.clusters[glyphs_range.start()]
1295 };
1296 for gi in glyphs_range {
1297 self.glyphs[gi].point.x -= offset;
1298
1299 if matches!(self.justify, Justify::InterLetter) && self.clusters[gi] != cluster {
1300 cluster = self.clusters[gi];
1301 offset += justify_advance;
1302 self.segments.0[gsi].advance -= justify_advance;
1303 }
1304 }
1305
1306 let seg = &mut self.segments.0[gsi];
1307 seg.x -= offset;
1308 if is_space {
1309 offset += justify_advance;
1310 seg.advance -= justify_advance;
1311 }
1312 }
1313 if last_is_space {
1314 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1315 self.segments.0[gsi].x -= offset;
1316 }
1317 }
1318
1319 self.justify = Justify::Auto;
1320
1321 if keep_alloc {
1322 drop(justified);
1323 self.justified = justified_alloc;
1324 }
1325 }
1326
1327 pub fn paragraph_break(&self) -> ParagraphBreak {
1329 self.paragraph_break
1330 }
1331
1332 pub fn line_height(&self) -> Px {
1334 self.line_height
1335 }
1336
1337 pub fn line_spacing(&self) -> Px {
1339 self.line_spacing
1340 }
1341
1342 pub fn paragraph_spacing(&self) -> Px {
1344 self.paragraph_spacing
1345 }
1346
1347 pub fn baseline(&self) -> Px {
1353 self.baseline
1354 }
1355
1356 pub fn overline(&self) -> Px {
1358 self.overline
1359 }
1360
1361 pub fn strikethrough(&self) -> Px {
1363 self.strikethrough
1364 }
1365
1366 pub fn underline(&self) -> Px {
1368 self.underline
1369 }
1370
1371 pub fn underline_descent(&self) -> Px {
1374 self.underline_descent
1375 }
1376
1377 pub fn is_empty(&self) -> bool {
1379 self.segments.0.is_empty()
1380 }
1381
1382 pub fn lines(&self) -> impl Iterator<Item = ShapedLine<'_>> {
1386 let just_width = self.justify_mode().map(|_| self.align_size.width);
1387 self.lines.iter_segs().enumerate().map(move |(i, (w, r))| ShapedLine {
1388 text: self,
1389 seg_range: r,
1390 index: i,
1391 width: just_width.unwrap_or_else(|| Px(w.round() as i32)),
1392 })
1393 }
1394
1395 pub fn lines_len(&self) -> usize {
1397 self.lines.0.len()
1398 }
1399
1400 pub fn first_wrapped(&self) -> bool {
1402 self.first_wrapped
1403 }
1404
1405 pub fn line(&self, line_idx: usize) -> Option<ShapedLine<'_>> {
1407 if line_idx >= self.lines.0.len() {
1408 None
1409 } else {
1410 self.lines.iter_segs_skip(line_idx).next().map(move |(w, r)| ShapedLine {
1411 text: self,
1412 seg_range: r,
1413 index: line_idx,
1414 width: Px(w.round() as i32),
1415 })
1416 }
1417 }
1418
1419 pub fn empty(&self) -> ShapedText {
1421 ShapedText {
1422 glyphs: vec![],
1423 clusters: vec![],
1424 segments: GlyphSegmentVec(vec![]),
1425 lines: LineRangeVec(vec![LineRange {
1426 end: 0,
1427 width: 0.0,
1428 x_offset: 0.0,
1429 directions: LayoutDirections::empty(),
1430 }]),
1431 fonts: FontRangeVec(vec![FontRange {
1432 font: self.fonts.font(0).clone(),
1433 end: 0,
1434 }]),
1435 images: vec![],
1436 orig_line_height: self.orig_line_height,
1437 orig_line_spacing: self.orig_line_spacing,
1438 orig_first_line: PxSize::zero(),
1439 orig_last_line: PxSize::zero(),
1440 line_height: self.orig_line_height,
1441 line_spacing: self.orig_line_spacing,
1442 paragraph_spacing: self.paragraph_spacing,
1443 paragraph_break: self.paragraph_break,
1444 baseline: self.baseline,
1445 overline: self.overline,
1446 strikethrough: self.strikethrough,
1447 underline: self.underline,
1448 underline_descent: self.underline_descent,
1449 mid_offset: 0.0,
1450 align_size: PxSize::zero(),
1451 align: Align::TOP_LEFT,
1452 justify: Justify::Auto,
1453 justified: vec![],
1454 overflow_align: Align::TOP_LEFT,
1455 direction: LayoutDirection::LTR,
1456 first_wrapped: false,
1457 first_line: PxRect::zero(),
1458 mid_clear: Px(0),
1459 is_inlined: false,
1460 mid_size: PxSize::zero(),
1461 last_line: PxRect::zero(),
1462 has_colored_glyphs: false,
1463 }
1464 }
1465
1466 pub fn can_rewrap(&self, max_width: Px) -> bool {
1470 for line in self.lines() {
1471 if line.width > max_width || line.started_by_wrap() {
1472 return true;
1473 }
1474 }
1475 false
1476 }
1477
1478 fn debug_assert_ranges(&self) {
1479 #[cfg(debug_assertions)]
1480 {
1481 #[allow(unused)]
1482 macro_rules! trace_assert {
1483 ($cond:expr $(,)?) => {
1484 #[allow(clippy::all)]
1485 if !($cond) {
1486 tracing::error!("{}", stringify!($cond));
1487 return;
1488 }
1489 };
1490 ($cond:expr, $($arg:tt)+) => {
1491 #[allow(clippy::all)]
1492 if !($cond) {
1493 tracing::error!($($arg)*);
1494 return;
1495 }
1496 };
1497 }
1498
1499 let mut prev_seg_end = 0;
1500 for seg in &self.segments.0 {
1501 trace_assert!(seg.end >= prev_seg_end);
1502 prev_seg_end = seg.end;
1503 }
1504 trace_assert!(self.segments.0.last().map(|s| s.end == self.glyphs.len()).unwrap_or(true));
1505
1506 let mut prev_line_end = 0;
1507 for (i, line) in self.lines.0.iter().enumerate() {
1508 trace_assert!(line.end >= prev_line_end);
1509 trace_assert!(line.width >= 0.0);
1510
1511 let line_max = line.x_offset + line.width;
1512 let glyphs = self.segments.glyphs_range(IndexRange(prev_line_end, line.end));
1513 for g in &self.glyphs[glyphs.iter()] {
1514 trace_assert!(
1518 g.point.x <= line_max,
1519 "glyph.x({:?}) > line[{i}].x+width({:?})",
1520 g.point.x,
1521 line_max
1522 );
1523 }
1524
1525 let seg_width = self.segments.0[prev_line_end..line.end].iter().map(|s| s.advance).sum::<f32>();
1526 trace_assert!(
1527 seg_width <= line.width,
1528 "seg_width({:?}) > line[{i}].width({:?})",
1529 seg_width,
1530 line.width,
1531 );
1532
1533 prev_line_end = line.end;
1534 }
1535 trace_assert!(self.lines.0.last().map(|l| l.end == self.segments.0.len()).unwrap_or(true));
1536
1537 let mut prev_font_end = 0;
1538 for font in &self.fonts.0 {
1539 trace_assert!(font.end >= prev_font_end);
1540 prev_font_end = font.end;
1541 }
1542 trace_assert!(self.fonts.0.last().map(|f| f.end == self.glyphs.len()).unwrap_or(true));
1543 }
1544 }
1545
1546 pub fn caret_origin(&self, caret: CaretIndex, full_text: &str) -> PxPoint {
1548 let index = caret.index;
1549 let mut end_line = None;
1550 for line in self.line(caret.line).into_iter().chain(self.lines()) {
1551 for seg in line.segs() {
1552 let txt_range = seg.text_range();
1553 if !txt_range.contains(&index) {
1554 continue;
1555 }
1556 let local_index = index - txt_range.start;
1557 let is_rtl = seg.direction().is_rtl();
1558
1559 let seg_rect = seg.rect();
1560 let mut origin = seg_rect.origin;
1561
1562 let clusters = seg.clusters();
1563 let mut cluster_i = 0;
1564 let mut search_lig = true;
1565
1566 if is_rtl {
1567 for (i, c) in clusters.iter().enumerate().rev() {
1568 match (*c as usize).cmp(&local_index) {
1569 cmp::Ordering::Less => {
1570 cluster_i = i;
1571 }
1572 cmp::Ordering::Equal => {
1573 cluster_i = i;
1574 search_lig = false;
1575 break;
1576 }
1577 cmp::Ordering::Greater => break,
1578 }
1579 }
1580 } else {
1581 for (i, c) in clusters.iter().enumerate() {
1582 match (*c as usize).cmp(&local_index) {
1583 cmp::Ordering::Less => {
1584 cluster_i = i;
1585 }
1586 cmp::Ordering::Equal => {
1587 cluster_i = i;
1588 search_lig = false;
1589 break;
1590 }
1591 cmp::Ordering::Greater => break,
1592 }
1593 }
1594 }
1595
1596 let mut origin_x = origin.x.0 as f32;
1597
1598 let mut glyph_take = cluster_i;
1604 if is_rtl {
1605 glyph_take += 1;
1606 }
1607
1608 let mut search_lig_data = None;
1609
1610 'outer: for (font, glyphs) in seg.glyphs_with_x_advance() {
1611 for (g, advance) in glyphs {
1612 search_lig_data = Some((font, g.index, advance));
1613
1614 if glyph_take == 0 {
1615 break 'outer;
1616 }
1617 origin_x += advance;
1618 glyph_take -= 1;
1619 }
1620 }
1621
1622 if search_lig && let Some((font, g_index, advance)) = search_lig_data {
1623 let lig_start = txt_range.start + clusters[cluster_i] as usize;
1624 let lig_end = if is_rtl {
1625 if cluster_i == 0 {
1626 txt_range.end
1627 } else {
1628 txt_range.start + clusters[cluster_i - 1] as usize
1629 }
1630 } else {
1631 clusters
1632 .get(cluster_i + 1)
1633 .map(|c| txt_range.start + *c as usize)
1634 .unwrap_or_else(|| txt_range.end)
1635 };
1636
1637 let maybe_lig = &full_text[lig_start..lig_end];
1638
1639 let lig_len = unicode_segmentation::UnicodeSegmentation::grapheme_indices(maybe_lig, true).count();
1640 if lig_len > 1 {
1641 let lig_taken = &full_text[lig_start..index];
1644 let lig_taken = unicode_segmentation::UnicodeSegmentation::grapheme_indices(lig_taken, true).count();
1645
1646 for (i, lig_advance) in font.ligature_caret_offsets(g_index).enumerate() {
1647 if i == lig_taken {
1648 origin_x += lig_advance;
1650 search_lig = false;
1651 break;
1652 }
1653 }
1654
1655 if search_lig {
1656 let lig_advance = advance * (lig_taken as f32 / lig_len as f32);
1658
1659 if is_rtl {
1660 origin_x -= lig_advance;
1661 } else {
1662 origin_x += lig_advance;
1663 }
1664 }
1665 }
1666 }
1667
1668 origin.x = Px(origin_x.round() as _);
1669 return origin;
1670 }
1671
1672 if line.index == caret.line && line.text_range().end == index && line.ended_by_wrap() {
1673 end_line = Some(line.index);
1675 break;
1676 }
1677 }
1678
1679 let line_end = end_line.unwrap_or_else(|| self.lines_len().saturating_sub(1));
1681 if let Some(line) = self.line(line_end) {
1682 let rect = line.rect();
1683 if self.direction().is_rtl() {
1684 PxPoint::new(rect.min_x(), rect.min_y())
1686 } else {
1687 PxPoint::new(rect.max_x(), rect.min_y())
1689 }
1690 } else {
1691 PxPoint::zero()
1692 }
1693 }
1694
1695 pub fn nearest_line(&self, y: Px) -> Option<ShapedLine<'_>> {
1697 let first_line_max_y = self.first_line.max_y();
1698 if first_line_max_y >= y {
1699 self.line(0)
1700 } else if self.last_line.min_y() <= y {
1701 self.line(self.lines_len().saturating_sub(1))
1702 } else {
1703 let y = y - first_line_max_y;
1704 let line = (y / self.line_height()).0 as usize + 1;
1705 self.lines.iter_segs_skip(line).next().map(move |(w, r)| ShapedLine {
1706 text: self,
1707 seg_range: r,
1708 index: line,
1709 width: Px(w.round() as i32),
1710 })
1711 }
1712 }
1713
1714 pub fn snap_caret_line(&self, mut caret: CaretIndex) -> CaretIndex {
1718 for line in self.lines() {
1719 let range = line.text_range();
1720
1721 if range.start == caret.index {
1722 if line.started_by_wrap() {
1724 if caret.line >= line.index {
1725 caret.line = line.index;
1726 } else {
1727 caret.line = line.index.saturating_sub(1);
1728 }
1729 } else {
1730 caret.line = line.index;
1731 }
1732 return caret;
1733 } else if range.contains(&caret.index) {
1734 caret.line = line.index;
1736 return caret;
1737 }
1738 }
1739 caret.line = self.lines.0.len().saturating_sub(1);
1740 caret
1741 }
1742
1743 pub fn overflow_info(&self, max_size: PxSize, overflow_suffix_width: Px) -> Option<TextOverflowInfo> {
1745 let (last_line, overflow_line) = match self.overflow_line(max_size.height) {
1748 Some(l) => {
1749 if l.index == 0 {
1750 return Some(TextOverflowInfo {
1752 line: 0,
1753 text_char: 0,
1754 included_glyphs: smallvec::smallvec![],
1755 suffix_origin: l.rect().origin.cast().cast_unit(),
1756 });
1757 } else {
1758 (self.line(l.index - 1).unwrap(), l.index)
1759 }
1760 }
1761 None => (self.line(self.lines_len().saturating_sub(1))?, self.lines_len()),
1762 };
1763
1764 let max_width = max_size.width - overflow_suffix_width;
1767
1768 if last_line.width <= max_width {
1769 return if overflow_line < self.lines_len() {
1771 Some(TextOverflowInfo {
1772 line: overflow_line,
1773 text_char: last_line.text_range().end,
1774 included_glyphs: smallvec::smallvec_inline![0..last_line.glyphs_range().end()],
1775 suffix_origin: {
1776 let r = last_line.rect();
1777 let mut o = r.origin;
1778 match self.direction {
1779 LayoutDirection::LTR => o.x += r.width(),
1780 LayoutDirection::RTL => o.x -= overflow_suffix_width,
1781 }
1782 o.cast().cast_unit()
1783 },
1784 })
1785 } else {
1786 None
1787 };
1788 }
1789
1790 let directions = last_line.directions();
1791 if directions == LayoutDirections::BIDI {
1792 let mut included_glyphs = smallvec::SmallVec::<[ops::Range<usize>; 1]>::new_const();
1793
1794 let min_x = match self.direction {
1795 LayoutDirection::LTR => Px(0),
1796 LayoutDirection::RTL => last_line.rect().max_x() - max_width,
1797 };
1798 let max_x = min_x + max_width;
1799
1800 let mut end_seg = None;
1801
1802 for seg in last_line.segs() {
1803 let (x, width) = seg.x_width();
1804 let seg_max_x = x + width;
1805
1806 if x < max_x && seg_max_x >= min_x {
1807 let mut glyphs_range = seg.glyphs_range().iter();
1808 let mut text_range = seg.text_range();
1809 if x < min_x {
1810 if let Some((c, g)) = seg.overflow_char_glyph((width - (min_x - x)).0 as f32) {
1811 glyphs_range.start += g + 1;
1812 text_range.start += c;
1813 }
1814 } else if seg_max_x > max_x
1815 && let Some((c, g)) = seg.overflow_char_glyph((width - seg_max_x - max_x).0 as f32)
1816 {
1817 glyphs_range.end -= g;
1818 text_range.end -= c;
1819 }
1820
1821 if let Some(l) = included_glyphs.last_mut() {
1822 if l.end == glyphs_range.start {
1823 l.end = glyphs_range.end;
1824 } else if glyphs_range.end == l.start {
1825 l.start = glyphs_range.start;
1826 } else {
1827 included_glyphs.push(glyphs_range.clone());
1828 }
1829 } else {
1830 included_glyphs.push(glyphs_range.clone());
1831 }
1832
1833 match self.direction {
1834 LayoutDirection::LTR => {
1835 if let Some((sx, se, gr, tr)) = &mut end_seg {
1836 if x < *sx {
1837 *sx = x;
1838 *se = seg;
1839 *gr = glyphs_range;
1840 *tr = text_range;
1841 }
1842 } else {
1843 end_seg = Some((x, seg, glyphs_range, text_range));
1844 }
1845 }
1846 LayoutDirection::RTL => {
1847 if let Some((smx, se, gr, tr)) = &mut end_seg {
1848 if seg_max_x < *smx {
1849 *smx = seg_max_x;
1850 *se = seg;
1851 *gr = glyphs_range;
1852 *tr = text_range;
1853 }
1854 } else {
1855 end_seg = Some((seg_max_x, seg, glyphs_range, text_range));
1856 }
1857 }
1858 }
1859 }
1860 }
1861
1862 if let Some((_, seg, glyphs_range, text_range)) = end_seg {
1863 Some(match self.direction {
1864 LayoutDirection::LTR => TextOverflowInfo {
1865 line: overflow_line,
1866 text_char: text_range.end,
1867 included_glyphs,
1868 suffix_origin: {
1869 let r = seg.rect();
1870 let seg_range = seg.glyphs_range().iter();
1871 let mut o = r.origin.cast().cast_unit();
1872 let mut w = r.width();
1873 if seg_range != glyphs_range
1874 && let Some(g) = seg.glyph(glyphs_range.end - seg_range.start)
1875 {
1876 o.x = g.1.point.x;
1877 w = Px(0);
1878 }
1879 o.x += w.0 as f32;
1880 o
1881 },
1882 },
1883 LayoutDirection::RTL => TextOverflowInfo {
1884 line: overflow_line,
1885 text_char: text_range.start,
1886 included_glyphs,
1887 suffix_origin: {
1888 let r = seg.rect();
1889 let mut o = r.origin.cast().cast_unit();
1890 let seg_range = seg.glyphs_range().iter();
1891 if seg_range != glyphs_range
1892 && let Some(g) = seg.glyph(glyphs_range.start - seg_range.start)
1893 {
1894 o.x = g.1.point.x;
1895 }
1896 o.x -= overflow_suffix_width.0 as f32;
1897 o
1898 },
1899 },
1900 })
1901 } else {
1902 None
1903 }
1904 } else {
1905 let mut max_width_f32 = max_width.0 as f32;
1907 for seg in last_line.segs() {
1908 let seg_advance = seg.advance();
1909 max_width_f32 -= seg_advance;
1910 if max_width_f32 <= 0.0 {
1911 let seg_text_range = seg.text_range();
1912 let seg_glyphs_range = seg.glyphs_range();
1913
1914 if directions == LayoutDirections::RTL {
1915 let (c, g) = match seg.overflow_char_glyph(seg_advance + max_width_f32) {
1916 Some(r) => r,
1917 None => (seg_text_range.len(), seg_glyphs_range.len()),
1918 };
1919
1920 return Some(TextOverflowInfo {
1921 line: overflow_line,
1922 text_char: seg_text_range.start + c,
1923 included_glyphs: smallvec::smallvec![
1924 0..seg_glyphs_range.start(),
1925 seg_glyphs_range.start() + g + 1..seg_glyphs_range.end()
1926 ],
1927 suffix_origin: {
1928 let mut o = if let Some(g) = seg.glyph(g + 1) {
1929 euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1930 } else {
1931 let rect = seg.rect();
1932 let mut o = rect.origin.cast().cast_unit();
1933 o.x += seg.advance();
1934 o
1935 };
1936 o.x -= overflow_suffix_width.0 as f32;
1937 o
1938 },
1939 });
1940 } else {
1941 let (c, g) = match seg.overflow_char_glyph((max_width - seg.x_width().0).0 as f32) {
1944 Some(r) => r,
1945 None => (seg_text_range.len(), seg_glyphs_range.len()),
1946 };
1947
1948 return Some(TextOverflowInfo {
1949 line: overflow_line,
1950 text_char: seg_text_range.start + c,
1951 included_glyphs: smallvec::smallvec_inline![0..seg_glyphs_range.start() + g],
1952 suffix_origin: {
1953 if let Some(g) = seg.glyph(g) {
1954 euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1955 } else {
1956 let rect = seg.rect();
1957 let mut o = rect.origin.cast().cast_unit();
1958 o.x += seg.advance();
1959 o
1960 }
1961 },
1962 });
1963 }
1964 }
1965 }
1966 None
1968 }
1969 }
1970
1971 pub fn highlight_rects(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = PxRect> + '_ {
1973 let start_origin = self.caret_origin(range.start, full_txt).x;
1974 let end_origin = self.caret_origin(range.end, full_txt).x;
1975
1976 MergingRectIter::new(
1977 self.lines()
1978 .skip(range.start.line)
1979 .take(range.end.line + 1 - range.start.line)
1980 .flat_map(|l| l.segs())
1981 .skip_while(move |s| s.text_end() <= range.start.index)
1982 .take_while(move |s| s.text_start() < range.end.index)
1983 .map(move |s| {
1984 let mut r = s.rect();
1985
1986 if s.text_start() <= range.start.index {
1987 match s.direction() {
1990 LayoutDirection::LTR => {
1991 r.size.width = r.max_x() - start_origin;
1992 r.origin.x = start_origin;
1993 }
1994 LayoutDirection::RTL => {
1995 r.size.width = start_origin - r.origin.x;
1996 }
1997 }
1998 }
1999 if s.text_end() > range.end.index {
2000 match s.direction() {
2003 LayoutDirection::LTR => {
2004 r.size.width = end_origin - r.origin.x;
2005 }
2006 LayoutDirection::RTL => {
2007 r.size.width = r.max_x() - end_origin;
2008 r.origin.x = end_origin;
2009 }
2010 }
2011 }
2012
2013 r
2014 }),
2015 )
2016 }
2017
2018 pub fn clip_lines(
2020 &self,
2021 clip_range: ops::Range<CaretIndex>,
2022 clip_out: bool,
2023 txt: &str,
2024 lines: impl Iterator<Item = (PxPoint, Px)>,
2025 ) -> Vec<(PxPoint, Px)> {
2026 let clips: Vec<_> = self.highlight_rects(clip_range, txt).collect();
2027
2028 let mut out_lines = vec![];
2029
2030 if clip_out {
2031 let mut exclude_buf = vec![];
2032 for (origin, width) in lines {
2033 let line_max = origin.x + width;
2034
2035 for clip in clips.iter() {
2036 if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2037 if origin.x < clip.max_x() && line_max > clip.origin.x {
2039 exclude_buf.push((clip.origin.x, clip.max_x()));
2041 }
2042 }
2043 }
2044
2045 if !exclude_buf.is_empty() {
2046 exclude_buf.sort_by_key(|(s, _)| *s);
2048
2049 if origin.x < exclude_buf[0].0 {
2050 out_lines.push((origin, exclude_buf[0].0 - origin.x));
2052 }
2053 let mut blank_start = exclude_buf[0].1;
2054 for (clip_start, clip_end) in exclude_buf.drain(..).skip(1) {
2055 if clip_start > blank_start {
2056 if line_max > clip_start {
2058 out_lines.push((PxPoint::new(blank_start, origin.y), line_max.min(clip_start) - blank_start));
2060 }
2061 blank_start = clip_end;
2062 }
2063 }
2064 if line_max > blank_start {
2065 out_lines.push((PxPoint::new(blank_start, origin.y), line_max - blank_start));
2067 }
2068 } else {
2069 out_lines.push((origin, width));
2071 }
2072 }
2073 } else {
2074 let mut include_buf = vec![];
2075 for (origin, width) in lines {
2076 let line_max = origin.x + width;
2077
2078 for clip in clips.iter() {
2079 if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2080 if origin.x < clip.max_x() && line_max > clip.origin.x {
2082 include_buf.push((clip.origin.x, clip.max_x()));
2084 }
2085 }
2086 }
2087
2088 if !include_buf.is_empty() {
2089 include_buf.sort_by_key(|(s, _)| *s);
2090
2091 for (clip_start, clip_end) in include_buf.drain(..) {
2092 let start = clip_start.max(origin.x);
2093 let end = clip_end.min(line_max);
2094
2095 out_lines.push((PxPoint::new(start, origin.y), end - start));
2096 }
2097
2098 include_buf.clear();
2099 }
2100 }
2101 }
2102
2103 out_lines
2104 }
2105}
2106
2107struct ImageGlyphsIter<'a, G>
2108where
2109 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2110{
2111 glyphs: G,
2112 glyphs_i: u32,
2113 images: &'a [(u32, GlyphImage)],
2114 maybe_img: Option<(&'a Font, &'a [GlyphInstance])>,
2115}
2116impl<'a, G> Iterator for ImageGlyphsIter<'a, G>
2117where
2118 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2119{
2120 type Item = (&'a Font, ShapedImageGlyphs<'a>);
2121
2122 fn next(&mut self) -> Option<Self::Item> {
2123 loop {
2124 if let Some((font, glyphs)) = &mut self.maybe_img {
2125 while self.images.first().map(|(i, _)| *i < self.glyphs_i).unwrap_or(false) {
2129 self.images = &self.images[1..];
2130 }
2131
2132 if let Some((i, img)) = self.images.first() {
2133 if *i == self.glyphs_i {
2135 self.glyphs_i += 1;
2137 let mut size = img.0.with(|i| i.size()).cast::<f32>();
2138 let scale = font.size().0 as f32 / size.width.max(size.height);
2139 size *= scale;
2140 let r = (
2141 *font,
2142 ShapedImageGlyphs::Image {
2143 rect: euclid::Rect::new(glyphs[0].point - euclid::vec2(0.0, size.height), size),
2144 base_glyph: glyphs[0].index,
2145 img: &img.0,
2146 },
2147 );
2148 *glyphs = &glyphs[1..];
2149 if glyphs.is_empty() {
2150 self.maybe_img = None;
2151 }
2152 return Some(r);
2153 } else {
2154 let normal = &glyphs[..glyphs.len().min(*i as _)];
2156 self.glyphs_i += normal.len() as u32;
2157
2158 *glyphs = &glyphs[normal.len()..];
2159 let r = (*font, ShapedImageGlyphs::Normal(normal));
2160
2161 if glyphs.is_empty() {
2162 self.maybe_img = None;
2163 }
2164 return Some(r);
2165 }
2166 } else {
2167 let r = (*font, ShapedImageGlyphs::Normal(glyphs));
2169 self.maybe_img = None;
2170 return Some(r);
2171 }
2172 } else if let Some(seq) = self.glyphs.next() {
2173 self.maybe_img = Some(seq);
2175 } else {
2176 return None;
2178 }
2179 }
2180 }
2181}
2182
2183struct ColoredGlyphsIter<'a, G>
2184where
2185 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2186{
2187 glyphs: G,
2188 maybe_colored: Option<(&'a Font, &'a [GlyphInstance])>,
2189}
2190impl<'a, G> Iterator for ColoredGlyphsIter<'a, G>
2191where
2192 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2193{
2194 type Item = (&'a Font, ShapedColoredGlyphs<'a>);
2195
2196 fn next(&mut self) -> Option<Self::Item> {
2197 loop {
2198 if let Some((font, glyphs)) = self.maybe_colored {
2199 let color_glyphs = font.face().color_glyphs();
2202
2203 for (i, g) in glyphs.iter().enumerate() {
2204 if let Some(c_glyphs) = color_glyphs.glyph(g.index) {
2205 let next_start = i + 1;
2208 if next_start < glyphs.len() {
2209 self.maybe_colored = Some((font, &glyphs[next_start..]));
2211 } else {
2212 self.maybe_colored = None;
2214 }
2215
2216 return Some((
2217 font,
2218 ShapedColoredGlyphs::Colored {
2219 point: g.point,
2220 base_glyph: g.index,
2221 glyphs: c_glyphs,
2222 },
2223 ));
2224 }
2225 }
2226 self.maybe_colored = None;
2228
2229 debug_assert!(!glyphs.is_empty());
2231 return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2232 } else if let Some((font, glyphs)) = self.glyphs.next() {
2233 let color_glyphs = font.face().color_glyphs();
2236 if color_glyphs.is_empty() {
2237 return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2238 } else {
2239 self.maybe_colored = Some((font, glyphs));
2241 continue;
2242 }
2243 } else {
2244 return None;
2245 }
2246 }
2247 }
2248}
2249
2250#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2254#[non_exhaustive]
2255pub struct TextOverflowInfo {
2256 pub line: usize,
2263
2264 pub text_char: usize,
2269
2270 pub included_glyphs: smallvec::SmallVec<[ops::Range<usize>; 1]>,
2275
2276 pub suffix_origin: euclid::Point2D<f32, Px>,
2281}
2282
2283trait FontListRef {
2284 fn shape_segment<R>(
2286 &self,
2287 seg: &str,
2288 word_ctx_key: &WordContextKey,
2289 features: &[rustybuzz::Feature],
2290 out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2291 ) -> R;
2292}
2293impl FontListRef for [Font] {
2294 fn shape_segment<R>(
2295 &self,
2296 seg: &str,
2297 word_ctx_key: &WordContextKey,
2298 features: &[rustybuzz::Feature],
2299 out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2300 ) -> R {
2301 let mut out = Some(out);
2302 let last = self.len() - 1;
2303 for font in &self[..last] {
2304 let r = font.shape_segment(seg, word_ctx_key, features, |seg| {
2305 if seg.glyphs.iter().all(|g| g.index != 0) {
2306 Some(out.take().unwrap()(seg, font))
2307 } else {
2308 None
2309 }
2310 });
2311 if let Some(r) = r {
2312 return r;
2313 }
2314 }
2315 self[last].shape_segment(seg, word_ctx_key, features, move |seg| out.unwrap()(seg, &self[last]))
2316 }
2317}
2318
2319struct ShapedTextBuilder {
2320 out: ShapedText,
2321
2322 line_height: f32,
2323 line_spacing: f32,
2324 paragraph_spacing: f32,
2325 word_spacing: f32,
2326 letter_spacing: f32,
2327 paragraph_indent: (f32, bool),
2328 max_width: f32,
2329 break_words: bool,
2330 hyphen_glyphs: (ShapedSegmentData, Font),
2331 tab_x_advance: f32,
2332 tab_index: u32,
2333 hyphens: Hyphens,
2334 lang: Lang,
2335
2336 origin: euclid::Point2D<f32, ()>,
2337 allow_first_wrap: bool,
2338 first_line_max: f32,
2339 mid_clear_min: f32,
2340 max_line_x: f32,
2341 text_seg_end: usize,
2342 line_has_ltr: bool,
2343 line_has_rtl: bool,
2344}
2345impl ShapedTextBuilder {
2346 fn actual_max_width(&self) -> f32 {
2347 if self.out.lines.0.is_empty() && !self.out.first_wrapped {
2348 self.first_line_max.min(self.max_width)
2349 } else {
2350 self.max_width
2351 }
2352 }
2353
2354 fn shape_text(fonts: &[Font], text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
2355 let mut t = Self {
2356 out: ShapedText {
2357 glyphs: Default::default(),
2358 clusters: Default::default(),
2359 segments: Default::default(),
2360 lines: Default::default(),
2361 fonts: Default::default(),
2362 line_height: Default::default(),
2363 line_spacing: Default::default(),
2364 orig_line_height: Default::default(),
2365 orig_line_spacing: Default::default(),
2366 orig_first_line: Default::default(),
2367 orig_last_line: Default::default(),
2368 paragraph_break: Default::default(),
2369 paragraph_spacing: Default::default(),
2370 baseline: Default::default(),
2371 overline: Default::default(),
2372 strikethrough: Default::default(),
2373 underline: Default::default(),
2374 underline_descent: Default::default(),
2375 mid_offset: 0.0,
2376 align_size: PxSize::zero(),
2377 align: Align::TOP_LEFT,
2378 justify: Justify::Auto,
2379 justified: vec![],
2380 overflow_align: Align::TOP_LEFT,
2381 direction: LayoutDirection::LTR,
2382 first_wrapped: false,
2383 is_inlined: config.inline_constraints.is_some(),
2384 first_line: PxRect::zero(),
2385 mid_clear: Px(0),
2386 mid_size: PxSize::zero(),
2387 last_line: PxRect::zero(),
2388 has_colored_glyphs: false,
2389 images: vec![],
2390 },
2391
2392 line_height: 0.0,
2393 line_spacing: 0.0,
2394 paragraph_spacing: 0.0,
2395 word_spacing: 0.0,
2396 letter_spacing: 0.0,
2397 paragraph_indent: (0.0, false),
2398 max_width: 0.0,
2399 break_words: false,
2400 hyphen_glyphs: (ShapedSegmentData::default(), fonts[0].clone()),
2401 tab_x_advance: 0.0,
2402 tab_index: 0,
2403 hyphens: config.hyphens,
2404 lang: config.lang.clone(),
2405 allow_first_wrap: false,
2406
2407 origin: euclid::point2(0.0, 0.0),
2408 first_line_max: f32::INFINITY,
2409 mid_clear_min: 0.0,
2410 max_line_x: 0.0,
2411 text_seg_end: 0,
2412 line_has_ltr: false,
2413 line_has_rtl: false,
2414 };
2415
2416 let mut word_ctx_key = WordContextKey::new(&config.lang, config.direction, &config.font_features);
2417
2418 let metrics = fonts[0].metrics();
2419
2420 t.out.orig_line_height = config.line_height;
2421 t.out.orig_line_spacing = config.line_spacing;
2422 t.out.line_height = config.line_height;
2423 t.out.line_spacing = config.line_spacing;
2424 t.out.paragraph_spacing = config.paragraph_spacing;
2425 t.out.paragraph_break = config.paragraph_break;
2426
2427 t.line_height = config.line_height.0 as f32;
2428 t.line_spacing = config.line_spacing.0 as f32;
2429 t.paragraph_spacing = config.paragraph_spacing.0 as f32;
2430 t.paragraph_indent = (config.paragraph_indent.0.0 as f32, config.paragraph_indent.1);
2431 let baseline = metrics.ascent + metrics.line_gap / 2.0;
2432
2433 t.out.baseline = t.out.line_height - baseline;
2434 t.out.underline = t.out.baseline + metrics.underline_position;
2435 t.out.underline_descent = t.out.baseline + metrics.descent + Px(1);
2436 t.out.strikethrough = t.out.baseline + metrics.ascent / 3.0;
2437 t.out.overline = t.out.baseline + metrics.ascent;
2438
2439 let dft_line_height = metrics.line_height().0 as f32;
2440 let center_height = (t.line_height - dft_line_height) / 2.0;
2441
2442 t.origin = euclid::point2::<_, ()>(0.0, baseline.0 as f32 + center_height);
2443 if !t.paragraph_indent.1 {
2444 t.origin.x = t.paragraph_indent.0;
2446 }
2447
2448 t.max_line_x = 0.0;
2449 if let Some(inline) = config.inline_constraints {
2450 t.first_line_max = inline.first_max.0 as f32;
2451 t.mid_clear_min = inline.mid_clear_min.0 as f32;
2452 t.allow_first_wrap = true;
2453 } else {
2454 t.first_line_max = f32::INFINITY;
2455 t.mid_clear_min = 0.0;
2456 t.allow_first_wrap = false;
2457 }
2458
2459 t.letter_spacing = config.letter_spacing.0 as f32;
2460 t.word_spacing = config.word_spacing.0 as f32;
2461 t.tab_x_advance = config.tab_x_advance.0 as f32;
2462 t.tab_index = fonts[0].space_index();
2463
2464 t.max_width = if config.max_width == Px::MAX {
2465 f32::INFINITY
2466 } else {
2467 config.max_width.0 as f32
2468 };
2469
2470 t.break_words = match config.word_break {
2471 WordBreak::Normal => {
2472 lang!("ch").matches(&config.lang, true, false)
2473 || lang!("jp").matches(&config.lang, true, false)
2474 || lang!("ko").matches(&config.lang, true, false)
2475 }
2476 WordBreak::BreakAll => true,
2477 WordBreak::KeepAll => false,
2478 };
2479
2480 if !matches!(config.hyphens, Hyphens::None) && t.max_width.is_finite() && config.obscuring_char.is_none() {
2481 t.hyphen_glyphs = fonts.shape_segment(config.hyphen_char.as_str(), &word_ctx_key, &config.font_features, |s, f| {
2483 (s.clone(), f.clone())
2484 });
2485 }
2486
2487 if let Some(c) = config.obscuring_char {
2488 t.push_obscured_text(fonts, &config.font_features, &mut word_ctx_key, text, c);
2489 } else {
2490 t.push_text(fonts, &config.font_features, &mut word_ctx_key, text);
2491 }
2492
2493 t.out.glyphs.shrink_to_fit();
2494 t.out.clusters.shrink_to_fit();
2495 t.out.segments.0.shrink_to_fit();
2496 t.out.lines.0.shrink_to_fit();
2497 t.out.fonts.0.shrink_to_fit();
2498 t.out.images.shrink_to_fit();
2499
2500 t.out.debug_assert_ranges();
2501 t.out
2502 }
2503
2504 fn push_obscured_text(
2505 &mut self,
2506 fonts: &[Font],
2507 features: &RFontFeatures,
2508 word_ctx_key: &mut WordContextKey,
2509 text: &SegmentedText,
2510 obscuring_char: char,
2511 ) {
2512 if text.is_empty() {
2513 self.push_last_line(text);
2514 self.push_font(&fonts[0]);
2515 return;
2516 }
2517
2518 let (glyphs, font) = fonts.shape_segment(Txt::from_char(obscuring_char).as_str(), word_ctx_key, features, |s, f| {
2519 (s.clone(), f.clone())
2520 });
2521
2522 for (seg, info) in text.iter() {
2523 let mut seg_glyphs = ShapedSegmentData::default();
2524 for (cluster, _) in seg.char_indices() {
2525 let i = seg_glyphs.glyphs.len();
2526 seg_glyphs.glyphs.extend(glyphs.glyphs.iter().copied());
2527 for g in &mut seg_glyphs.glyphs[i..] {
2528 g.point.0 += seg_glyphs.x_advance;
2529 g.cluster = cluster as u32;
2530 }
2531 seg_glyphs.x_advance += glyphs.x_advance;
2532 }
2533 self.push_glyphs(&seg_glyphs, self.letter_spacing);
2534 self.push_text_seg(seg, info);
2535 }
2536
2537 self.push_last_line(text);
2538
2539 self.push_font(&font);
2540 }
2541
2542 fn push_text(&mut self, fonts: &[Font], features: &RFontFeatures, word_ctx_key: &mut WordContextKey, text: &SegmentedText) {
2543 static LIG: [&[u8]; 4] = [b"liga", b"clig", b"dlig", b"hlig"];
2544 let ligature_enabled = fonts[0].face().has_ligatures()
2545 && features.iter().any(|f| {
2546 let tag = f.tag.to_bytes();
2547 LIG.iter().any(|l| *l == tag)
2548 });
2549
2550 if ligature_enabled {
2551 let mut start = 0;
2552 let mut words_start = None;
2553 for (i, info) in text.segs().iter().enumerate() {
2554 if info.kind.is_word() && info.kind != TextSegmentKind::Emoji {
2555 if words_start.is_none() {
2556 words_start = Some(i);
2557 }
2558 } else {
2559 if let Some(s) = words_start.take() {
2560 self.push_ligature_words(fonts, features, word_ctx_key, text, s, i);
2561 }
2562
2563 let seg = &text.text()[start..info.end];
2564 self.push_seg(fonts, features, word_ctx_key, text, seg, *info);
2565 }
2566 start = info.end;
2567 }
2568 if let Some(s) = words_start.take() {
2569 self.push_ligature_words(fonts, features, word_ctx_key, text, s, text.segs().len());
2570 }
2571 } else {
2572 for (seg, info) in text.iter() {
2573 self.push_seg(fonts, features, word_ctx_key, text, seg, info);
2574 }
2575 }
2576
2577 self.push_last_line(text);
2578
2579 self.push_font(&fonts[0]);
2580 }
2581 fn push_ligature_words(
2582 &mut self,
2583 fonts: &[Font],
2584 features: &RFontFeatures,
2585 word_ctx_key: &mut WordContextKey,
2586 text: &SegmentedText,
2587 words_start: usize,
2588 words_end: usize,
2589 ) {
2590 let seg_start = if words_start == 0 { 0 } else { text.segs()[words_start - 1].end };
2591 let end_info = text.segs()[words_end - 1];
2592 let seg_end = end_info.end;
2593 let seg = &text.text()[seg_start..seg_end];
2594
2595 if words_end - words_start == 1 {
2596 self.push_seg(fonts, features, word_ctx_key, text, seg, end_info);
2597 } else {
2598 let handled = fonts[0].shape_segment(seg, word_ctx_key, features, |shaped_seg| {
2600 let mut cluster_start = 0;
2601 let mut cluster_end = None;
2602 for g in shaped_seg.glyphs.iter() {
2603 if g.index == 0 {
2604 return false;
2606 }
2607 if seg[cluster_start as usize..g.cluster as usize].chars().take(2).count() > 1 {
2608 cluster_end = Some(g.index);
2609 break;
2610 }
2611 cluster_start = g.cluster;
2612 }
2613
2614 if cluster_end.is_none() && seg[cluster_start as usize..].chars().take(2).count() > 1 {
2615 cluster_end = Some(seg.len() as u32);
2616 }
2617 if let Some(cluster_end) = cluster_end {
2618 let cluster_start_in_txt = seg_start + cluster_start as usize;
2620 let cluster_end_in_txt = seg_start + cluster_end as usize;
2621
2622 let handle = text.segs()[words_start..words_end]
2623 .iter()
2624 .any(|info| info.end > cluster_start_in_txt && info.end <= cluster_end_in_txt);
2625
2626 if handle {
2627 let max_width = self.actual_max_width();
2628 if self.origin.x + shaped_seg.x_advance > max_width {
2629 if shaped_seg.x_advance > max_width {
2631 return false;
2633 }
2634
2635 self.push_line_break(true, text);
2636 self.push_glyphs(shaped_seg, self.letter_spacing);
2637 }
2638 self.push_glyphs(shaped_seg, self.letter_spacing);
2639 let mut seg = seg;
2640 for info in &text.segs()[words_start..words_end] {
2641 self.push_text_seg(seg, *info);
2642 seg = "";
2643 }
2644
2645 return true;
2646 }
2647 }
2648
2649 false
2650 });
2651
2652 if !handled {
2653 let mut seg_start = seg_start;
2654 for info in text.segs()[words_start..words_end].iter() {
2655 let seg = &text.text()[seg_start..info.end];
2656 self.push_seg(fonts, features, word_ctx_key, text, seg, *info);
2657 seg_start = info.end;
2658 }
2659 }
2660 }
2661 }
2662 fn push_seg(
2663 &mut self,
2664 fonts: &[Font],
2665 features: &RFontFeatures,
2666 word_ctx_key: &mut WordContextKey,
2667 text: &SegmentedText,
2668 seg: &str,
2669 info: TextSegment,
2670 ) {
2671 word_ctx_key.direction = info.direction();
2672 if info.kind.is_word() {
2673 let max_width = self.actual_max_width();
2674
2675 fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2676 if self.origin.x + shaped_seg.x_advance > max_width {
2677 if shaped_seg.x_advance > max_width {
2679 let hyphenated = self.push_hyphenate(seg, font, shaped_seg, info, text);
2683
2684 if !hyphenated && self.break_words {
2685 self.push_split_seg(shaped_seg, seg, info, self.letter_spacing, text);
2687 } else if !hyphenated {
2688 let current_start = if self.out.lines.0.is_empty() {
2689 0
2690 } else {
2691 self.out.lines.last().end
2692 };
2693 if !self.out.segments.0[current_start..].is_empty() {
2694 self.push_line_break(true, text);
2695 } else if current_start == 0 && self.allow_first_wrap {
2696 self.out.first_wrapped = true;
2697 }
2698 self.push_glyphs(shaped_seg, self.letter_spacing);
2699 self.push_text_seg(seg, info);
2700 }
2701 } else {
2702 self.push_line_break(true, text);
2703 self.push_glyphs(shaped_seg, self.letter_spacing);
2704 self.push_text_seg(seg, info);
2705 }
2706 } else {
2707 self.push_glyphs(shaped_seg, self.letter_spacing);
2709 self.push_text_seg(seg, info);
2710 }
2711
2712 if matches!(info.kind, TextSegmentKind::Emoji) {
2713 if !font.face().color_glyphs().is_empty() {
2714 self.out.has_colored_glyphs = true;
2715 }
2716 if (font.face().has_raster_images() || (cfg!(feature = "svg") && font.face().has_svg_images()))
2717 && let Some(ttf) = font.face().ttf()
2718 {
2719 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2720 let id = ttf_parser::GlyphId(g.index as _);
2721 let ppm = font.size().0 as u16;
2722 let glyphs_i = self.out.glyphs.len() - shaped_seg.glyphs.len() + i;
2723 if let Some(img) = ttf.glyph_raster_image(id, ppm) {
2724 self.push_glyph_raster(glyphs_i as _, img);
2725 } else if cfg!(feature = "svg")
2726 && let Some(img) = ttf.glyph_svg_image(id)
2727 {
2728 self.push_glyph_svg(glyphs_i as _, img);
2729 }
2730 }
2731 }
2732 }
2733
2734 self.push_font(font);
2735 });
2736 } else if info.kind.is_space() {
2737 if matches!(info.kind, TextSegmentKind::Tab) {
2738 let max_width = self.actual_max_width();
2739 for (i, _) in seg.char_indices() {
2740 if self.origin.x + self.tab_x_advance > max_width {
2741 self.push_line_break(true, text);
2743 }
2744 let point = euclid::point2(self.origin.x, self.origin.y);
2745 self.origin.x += self.tab_x_advance;
2746 self.out.glyphs.push(GlyphInstance::new(self.tab_index, point));
2747 self.out.clusters.push(i as u32);
2748 }
2749
2750 self.push_text_seg(seg, info);
2751 self.push_font(&fonts[0]);
2752 } else {
2753 let max_width = self.actual_max_width();
2754 fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2755 if self.origin.x + shaped_seg.x_advance > max_width {
2756 if seg.len() > 2 {
2758 self.push_split_seg(shaped_seg, seg, info, self.word_spacing, text);
2760 } else {
2761 self.push_line_break(true, text);
2762 self.push_glyphs(shaped_seg, self.word_spacing);
2763 self.push_text_seg(seg, info);
2764 }
2765 } else {
2766 self.push_glyphs(shaped_seg, self.word_spacing);
2767 self.push_text_seg(seg, info);
2768 }
2769
2770 self.push_font(font);
2771 });
2772 }
2773 } else if info.kind.is_line_break() {
2774 self.push_text_seg(seg, info);
2775 self.push_line_break(false, text);
2776 } else {
2777 self.push_text_seg(seg, info)
2778 }
2779 }
2780
2781 fn push_glyph_raster(&mut self, glyphs_i: u32, img: ttf_parser::RasterGlyphImage) {
2782 use ttf_parser::RasterImageFormat;
2783 let size = PxSize::new(Px(img.width as _), Px(img.height as _));
2784 let bgra_fmt = ImageDataFormat::Bgra8 { size, density: None };
2785 let bgra_len = img.width as usize * img.height as usize * 4;
2786 let (data, fmt) = match img.format {
2787 RasterImageFormat::PNG => (img.data.to_vec(), ImageDataFormat::from("png")),
2788 RasterImageFormat::BitmapMono => {
2789 let mut bgra = Vec::with_capacity(bgra_len);
2791 let bytes_per_row = (img.width as usize).div_ceil(8);
2792 for y in 0..img.height as usize {
2793 let row_start = y * bytes_per_row;
2794 for x in 0..img.width as usize {
2795 let byte_index = row_start + x / 8;
2796 let bit_index = 7 - (x % 8);
2797 let bit = (img.data[byte_index] >> bit_index) & 1;
2798 let color = if bit == 1 { [0, 0, 0, 255] } else { [255, 255, 255, 255] };
2799 bgra.extend_from_slice(&color);
2800 }
2801 }
2802 (bgra, bgra_fmt)
2803 }
2804 RasterImageFormat::BitmapMonoPacked => {
2805 let mut bgra = Vec::with_capacity(bgra_len);
2807 for &c8 in img.data {
2808 for bit in 0..8 {
2809 let color = if (c8 >> (7 - bit)) & 1 == 1 {
2810 [0, 0, 0, 255]
2811 } else {
2812 [255, 255, 255, 255]
2813 };
2814 bgra.extend_from_slice(&color);
2815 if bgra.len() == bgra_len {
2816 break;
2817 }
2818 }
2819 }
2820 (bgra, bgra_fmt)
2821 }
2822 RasterImageFormat::BitmapGray2 => {
2823 let mut bgra = Vec::with_capacity(bgra_len);
2825 let bytes_per_row = (img.width as usize).div_ceil(4);
2826 for y in 0..img.height as usize {
2827 let row_start = y * bytes_per_row;
2828 for x in 0..img.width as usize {
2829 let byte_index = row_start + x / 4;
2830 let shift = (3 - (x % 4)) * 2;
2831 let gray = (img.data[byte_index] >> shift) & 0b11;
2832 let color = match gray {
2833 0b00 => [0, 0, 0, 255], 0b01 => [85, 85, 85, 255], 0b10 => [170, 170, 170, 255], 0b11 => [255, 255, 255, 255], _ => unreachable!(),
2838 };
2839 bgra.extend_from_slice(&color);
2840 }
2841 }
2842 (bgra, bgra_fmt)
2843 }
2844 RasterImageFormat::BitmapGray2Packed => {
2845 let mut bgra = Vec::with_capacity(bgra_len);
2847 for &c4 in img.data {
2848 for i in 0..4 {
2849 let gray = (c4 >> (7 - i * 2)) & 0b11;
2850 let color = match gray {
2851 0b00 => [0, 0, 0, 255], 0b01 => [85, 85, 85, 255], 0b10 => [170, 170, 170, 255], 0b11 => [255, 255, 255, 255], _ => unreachable!(),
2856 };
2857 bgra.extend_from_slice(&color);
2858 if bgra.len() == bgra_len {
2859 break;
2860 }
2861 }
2862 }
2863 (bgra, bgra_fmt)
2864 }
2865 RasterImageFormat::BitmapGray4 => {
2866 let mut bgra = Vec::with_capacity(bgra_len);
2868 let bytes_per_row = (img.width as usize).div_ceil(2);
2869 for y in 0..img.height as usize {
2870 let row_start = y * bytes_per_row;
2871 for x in 0..img.width as usize {
2872 let byte_index = row_start + x / 2;
2873 let shift = if x % 2 == 0 { 4 } else { 0 };
2874 let gray = (img.data[byte_index] >> shift) & 0b1111;
2875 let g = gray * 17;
2876 bgra.extend_from_slice(&[g, g, g, 255]);
2877 }
2878 }
2879 (bgra, bgra_fmt)
2880 }
2881 RasterImageFormat::BitmapGray4Packed => {
2882 let mut bgra = Vec::with_capacity(bgra_len);
2883 for &c2 in img.data {
2884 for i in 0..2 {
2885 let gray = (c2 >> (7 - i * 4)) & 0b1111;
2886 let g = gray * 17;
2887 bgra.extend_from_slice(&[g, g, g, 255]);
2888 if bgra.len() == bgra_len {
2889 break;
2890 }
2891 }
2892 }
2893 (bgra, bgra_fmt)
2894 }
2895 RasterImageFormat::BitmapGray8 => {
2896 let mut bgra = Vec::with_capacity(bgra_len);
2897 for &c in img.data {
2898 bgra.extend_from_slice(&[c, c, c, 255]);
2899 }
2900 (bgra, bgra_fmt)
2901 }
2902 RasterImageFormat::BitmapPremulBgra32 => {
2903 let mut bgra = img.data.to_vec();
2904 for c in bgra.chunks_exact_mut(4) {
2905 let (b, g, r, a) = (c[0], c[1], c[2], c[3]);
2906 let unp = if a == 255 {
2907 [b, g, r]
2908 } else {
2909 [
2910 (b as u32 * 255 / a as u32) as u8,
2911 (g as u32 * 255 / a as u32) as u8,
2912 (r as u32 * 255 / a as u32) as u8,
2913 ]
2914 };
2915 c.copy_from_slice(&unp);
2916 }
2917 (bgra, bgra_fmt)
2918 }
2919 };
2920 self.push_glyph_img(glyphs_i, ImageSource::from((data, fmt)));
2921 }
2922
2923 fn push_glyph_svg(&mut self, glyphs_i: u32, img: ttf_parser::svg::SvgDocument) {
2924 self.push_glyph_img(glyphs_i, ImageSource::from((img.data, ImageDataFormat::from("svg"))));
2925 }
2926
2927 fn push_glyph_img(&mut self, glyphs_i: u32, source: ImageSource) {
2928 let img = IMAGES.cache(source);
2929
2930 self.out.images.push((glyphs_i, GlyphImage(img)));
2931 }
2932
2933 fn push_last_line(&mut self, text: &SegmentedText) {
2934 let directions = self.finish_current_line_bidi(text);
2935 self.out.lines.0.push(LineRange {
2936 end: self.out.segments.0.len(),
2937 width: self.origin.x,
2938 x_offset: 0.0,
2939 directions,
2940 });
2941
2942 self.out.update_mid_size();
2943 self.out.update_first_last_lines();
2944 self.out.orig_first_line = self.out.first_line.size;
2945 self.out.orig_last_line = self.out.last_line.size;
2946 if self.out.is_inlined && self.out.lines.0.len() > 1 {
2947 self.out.last_line.origin.y += self.out.mid_clear;
2948 }
2949 }
2950
2951 fn push_hyphenate(&mut self, seg: &str, font: &Font, shaped_seg: &ShapedSegmentData, info: TextSegment, text: &SegmentedText) -> bool {
2952 if !matches!(self.hyphens, Hyphens::Auto) {
2953 return false;
2954 }
2955
2956 let split_points = HYPHENATION.hyphenate(&self.lang, seg);
2957 self.push_hyphenate_pt(&split_points, 0, font, shaped_seg, seg, info, text)
2958 }
2959
2960 #[allow(clippy::too_many_arguments)]
2961 fn push_hyphenate_pt(
2962 &mut self,
2963 split_points: &[usize],
2964 split_points_sub: usize,
2965 font: &Font,
2966 shaped_seg: &ShapedSegmentData,
2967 seg: &str,
2968 info: TextSegment,
2969 text: &SegmentedText,
2970 ) -> bool {
2971 if split_points.is_empty() {
2972 return false;
2973 }
2974
2975 let mut end_glyph = 0;
2977 let mut end_point_i = 0;
2978 let max_width = self.actual_max_width();
2979 for (i, point) in split_points.iter().enumerate() {
2980 let mut point = *point - split_points_sub;
2981 let mut width = 0.0;
2982 let mut c = u32::MAX;
2983 let mut gi = 0;
2984 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2986 width = g.point.0;
2987 if g.cluster != c {
2988 if point == 0 {
2990 break;
2991 }
2992 c = g.cluster;
2993 point -= 1;
2994 }
2995 gi = i;
2996 }
2997
2998 if self.origin.x + width + self.hyphen_glyphs.0.x_advance > max_width {
2999 if end_glyph == 0 {
3001 end_glyph = gi + 1;
3003 end_point_i = i + 1;
3004 }
3005 break;
3006 } else {
3007 end_glyph = gi + 1;
3009 end_point_i = i + 1;
3010 }
3011 }
3012
3013 let end_glyph_x = shaped_seg.glyphs[end_glyph].point.0;
3015 let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3016
3017 if glyphs_a.is_empty() || glyphs_b.is_empty() {
3018 debug_assert!(false, "invalid hyphenation split");
3019 return false;
3020 }
3021 let end_cluster = glyphs_b[0].cluster;
3022 let (seg_a, seg_b) = seg.split_at(end_cluster as usize);
3023
3024 self.push_glyphs(
3025 &ShapedSegmentData {
3026 glyphs: glyphs_a.to_vec(),
3027 x_advance: end_glyph_x,
3028 y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3029 },
3030 self.word_spacing,
3031 );
3032 self.push_font(font);
3033
3034 self.push_glyphs(&self.hyphen_glyphs.0.clone(), 0.0);
3035 self.push_font(&self.hyphen_glyphs.1.clone());
3036
3037 self.push_text_seg(seg_a, info);
3038
3039 self.push_line_break(true, text);
3040
3041 let mut shaped_seg_b = ShapedSegmentData {
3043 glyphs: glyphs_b.to_vec(),
3044 x_advance: shaped_seg.x_advance - end_glyph_x,
3045 y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3046 };
3047 for g in &mut shaped_seg_b.glyphs {
3048 g.point.0 -= end_glyph_x;
3049 g.cluster -= seg_a.len() as u32;
3050 }
3051
3052 if shaped_seg_b.x_advance > self.actual_max_width() {
3053 if self.push_hyphenate_pt(
3055 &split_points[end_point_i..],
3056 split_points_sub + seg_a.len(),
3057 font,
3058 &shaped_seg_b,
3059 seg_b,
3060 info,
3061 text,
3062 ) {
3063 return true;
3064 }
3065 }
3066
3067 self.push_glyphs(&shaped_seg_b, self.word_spacing);
3069 self.push_text_seg(seg_b, info);
3070 true
3071 }
3072
3073 fn push_glyphs(&mut self, shaped_seg: &ShapedSegmentData, spacing: f32) {
3074 self.out.glyphs.extend(shaped_seg.glyphs.iter().map(|gi| {
3075 let r = GlyphInstance::new(gi.index, euclid::point2(gi.point.0 + self.origin.x, gi.point.1 + self.origin.y));
3076 self.origin.x += spacing;
3077 r
3078 }));
3079 self.out.clusters.extend(shaped_seg.glyphs.iter().map(|gi| gi.cluster));
3080
3081 self.origin.x += shaped_seg.x_advance;
3082 self.origin.y += shaped_seg.y_advance;
3083 }
3084
3085 fn push_line_break(&mut self, soft: bool, text: &SegmentedText) {
3086 if self.out.glyphs.is_empty() && self.allow_first_wrap && soft {
3087 self.out.first_wrapped = true;
3088 } else {
3089 let directions = self.finish_current_line_bidi(text);
3090
3091 self.out.lines.0.push(LineRange {
3092 end: self.out.segments.0.len(),
3093 width: self.origin.x,
3094 x_offset: 0.0,
3095 directions,
3096 });
3097
3098 if self.out.lines.0.len() == 1 {
3099 self.out.first_line = PxRect::from_size(PxSize::new(Px(self.origin.x as i32), Px(self.line_height as i32)));
3100
3101 if !self.out.first_wrapped {
3102 let mid_clear = (self.mid_clear_min - self.line_height).max(0.0).round();
3103 self.origin.y += mid_clear;
3104 self.out.mid_clear = Px(mid_clear as i32);
3105 self.out.mid_offset = mid_clear;
3106 }
3107 }
3108
3109 self.max_line_x = self.origin.x.max(self.max_line_x);
3110
3111 let is_paragraph_break = match self.out.paragraph_break {
3112 ParagraphBreak::None => false,
3113 ParagraphBreak::Line => !soft,
3114 };
3115
3116 self.origin.x = if self.paragraph_indent.1 != is_paragraph_break {
3117 self.paragraph_indent.0
3119 } else {
3120 0.0
3121 };
3122
3123 self.origin.y += if is_paragraph_break {
3124 self.paragraph_spacing
3125 } else {
3126 self.line_height + self.line_spacing
3127 };
3128 }
3129 }
3130
3131 #[must_use]
3132 fn finish_current_line_bidi(&mut self, text: &SegmentedText) -> LayoutDirections {
3133 if self.line_has_rtl {
3134 let seg_start = if self.out.lines.0.is_empty() {
3135 0
3136 } else {
3137 self.out.lines.last().end
3138 };
3139
3140 if self.line_has_ltr {
3141 let line_segs = seg_start..self.out.segments.0.len();
3144
3145 let mut x = 0.0;
3147 for i in text.reorder_line_to_ltr(line_segs) {
3148 let g_range = self.out.segments.glyphs(i);
3149 if g_range.iter().is_empty() {
3150 continue;
3151 }
3152
3153 let glyphs = &mut self.out.glyphs[g_range.iter()];
3154 let offset = x - self.out.segments.0[i].x;
3155 self.out.segments.0[i].x = x;
3156 for g in glyphs {
3157 g.point.x += offset;
3158 }
3159 x += self.out.segments.0[i].advance;
3160 }
3161 } else {
3162 let line_width = self.origin.x;
3164
3165 let mut x = line_width;
3166 for i in seg_start..self.out.segments.0.len() {
3167 x -= self.out.segments.0[i].advance;
3168
3169 let g_range = self.out.segments.glyphs(i);
3170
3171 let glyphs = &mut self.out.glyphs[g_range.iter()];
3172 let offset = x - self.out.segments.0[i].x;
3173 self.out.segments.0[i].x = x;
3174 for g in glyphs {
3175 g.point.x += offset;
3176 }
3177 }
3178 }
3179 }
3180
3181 let mut d = LayoutDirections::empty();
3182 d.set(LayoutDirections::LTR, self.line_has_ltr);
3183 d.set(LayoutDirections::RTL, self.line_has_rtl);
3184
3185 self.line_has_ltr = false;
3186 self.line_has_rtl = false;
3187
3188 d
3189 }
3190
3191 pub fn push_text_seg(&mut self, seg: &str, info: TextSegment) {
3192 let g_len = if let Some(l) = self.out.segments.0.last() {
3193 self.out.glyphs.len() - l.end
3194 } else {
3195 self.out.glyphs.len()
3196 };
3197 if g_len > 0 {
3198 self.line_has_ltr |= info.level.is_ltr();
3199 self.line_has_rtl |= info.level.is_rtl();
3200 }
3201
3202 self.text_seg_end += seg.len();
3203
3204 let is_first_of_line =
3205 (!self.out.lines.0.is_empty() && self.out.lines.last().end == self.out.segments.0.len()) || self.out.segments.0.is_empty();
3206 let x = if is_first_of_line {
3207 0.0
3208 } else {
3209 self.out.segments.0.last().map(|s| s.x + s.advance).unwrap_or(0.0)
3211 };
3212 self.out.segments.0.push(GlyphSegment {
3213 text: TextSegment {
3214 end: self.text_seg_end,
3215 ..info
3216 },
3217 end: self.out.glyphs.len(),
3218 x,
3219 advance: self.origin.x - x,
3220 });
3221 }
3222
3223 pub fn push_split_seg(&mut self, shaped_seg: &ShapedSegmentData, seg: &str, info: TextSegment, spacing: f32, text: &SegmentedText) {
3224 let mut end_glyph = 0;
3225 let mut end_glyph_x = 0.0;
3226 let max_width = self.actual_max_width();
3227 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3228 if self.origin.x + g.point.0 > max_width {
3229 break;
3230 }
3231 end_glyph = i;
3232 end_glyph_x = g.point.0;
3233 }
3234
3235 let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3236
3237 if glyphs_a.is_empty() || glyphs_b.is_empty() {
3238 self.push_line_break(true, text);
3240 self.push_glyphs(shaped_seg, spacing);
3241 self.push_text_seg(seg, info);
3242 } else {
3243 let (seg_a, seg_b) = seg.split_at(glyphs_b[0].cluster as usize);
3244
3245 let shaped_seg_a = ShapedSegmentData {
3246 glyphs: glyphs_a.to_vec(),
3247 x_advance: end_glyph_x,
3248 y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3249 };
3250 self.push_glyphs(&shaped_seg_a, spacing);
3251 self.push_text_seg(seg_a, info);
3252 self.push_line_break(true, text);
3253
3254 let mut shaped_seg_b = ShapedSegmentData {
3255 glyphs: glyphs_b.to_vec(),
3256 x_advance: shaped_seg.x_advance - end_glyph_x,
3257 y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3258 };
3259 for g in &mut shaped_seg_b.glyphs {
3260 g.point.0 -= shaped_seg_a.x_advance;
3261 g.cluster -= seg_a.len() as u32;
3262 }
3263
3264 if shaped_seg_b.x_advance <= max_width {
3265 self.push_glyphs(&shaped_seg_b, spacing);
3266 self.push_text_seg(seg_b, info);
3267 } else {
3268 self.push_split_seg(&shaped_seg_b, seg_b, info, spacing, text);
3269 }
3270 }
3271 }
3272
3273 fn push_font(&mut self, font: &Font) {
3274 if let Some(last) = self.out.fonts.0.last_mut() {
3275 if &last.font == font {
3276 last.end = self.out.glyphs.len();
3277 return;
3278 } else if last.end == self.out.glyphs.len() {
3279 return;
3280 }
3281 }
3282 self.out.fonts.0.push(FontRange {
3283 font: font.clone(),
3284 end: self.out.glyphs.len(),
3285 })
3286 }
3287}
3288
3289bitflags! {
3290 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3292 #[serde(transparent)]
3293 pub struct LayoutDirections: u8 {
3294 const LTR = 1;
3296 const RTL = 2;
3298 const BIDI = Self::LTR.bits() | Self::RTL.bits();
3302 }
3303}
3304
3305#[derive(Clone, Copy)]
3307pub struct ShapedLine<'a> {
3308 text: &'a ShapedText,
3309 seg_range: IndexRange,
3311 index: usize,
3312 width: Px,
3313}
3314impl fmt::Debug for ShapedLine<'_> {
3315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3316 f.debug_struct("ShapedLine")
3317 .field("seg_range", &self.seg_range)
3318 .field("index", &self.index)
3319 .field("width", &self.width)
3320 .finish_non_exhaustive()
3321 }
3322}
3323impl<'a> ShapedLine<'a> {
3324 pub fn height(&self) -> Px {
3326 if self.index == 0 {
3327 self.text.first_line.height()
3328 } else if self.index == self.text.lines.0.len() - 1 {
3329 self.text.last_line.height()
3330 } else {
3331 self.text.line_height
3332 }
3333 }
3334
3335 pub fn width(&self) -> Px {
3337 if self.index == 0 {
3338 self.text.first_line.width()
3339 } else if self.index == self.text.lines.0.len() - 1 {
3340 self.text.last_line.width()
3341 } else {
3342 self.width
3343 }
3344 }
3345
3346 pub fn rect(&self) -> PxRect {
3348 if self.index == 0 {
3349 return self.text.first_line;
3350 }
3351 if self.index == self.text.lines.0.len() - 1 {
3352 return self.text.last_line;
3353 }
3354
3355 let size = PxSize::new(self.width, self.text.line_height);
3356 let origin = PxPoint::new(
3357 Px(self.text.lines.0[self.index].x_offset as i32),
3358 self.text.line_height * Px((self.index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear,
3359 );
3360 PxRect::new(origin, size)
3361 }
3362
3363 pub fn original_size(&self) -> PxSize {
3370 if self.index == 0 {
3371 return self.text.orig_first_line;
3372 }
3373 if self.index == self.text.lines.0.len() - 1 {
3374 return self.text.orig_last_line;
3375 }
3376 PxSize::new(self.width, self.text.line_height)
3377 }
3378
3379 pub fn is_paragraph_start(&self) -> bool {
3385 match self.text.paragraph_break {
3386 ParagraphBreak::None => self.index == 0,
3387 ParagraphBreak::Line => !self.started_by_wrap(),
3388 }
3389 }
3390
3391 pub fn is_paragraph_end(&self) -> bool {
3397 match self.text.paragraph_break {
3398 ParagraphBreak::None => self.index == self.text.lines.0.len() - 1,
3399 ParagraphBreak::Line => !self.ended_by_wrap(),
3400 }
3401 }
3402
3403 pub fn overline(&self) -> (PxPoint, Px) {
3405 self.decoration_line(self.text.overline)
3406 }
3407
3408 pub fn strikethrough(&self) -> (PxPoint, Px) {
3410 self.decoration_line(self.text.strikethrough)
3411 }
3412
3413 pub fn underline(&self) -> (PxPoint, Px) {
3419 self.decoration_line(self.text.underline)
3420 }
3421
3422 pub fn underline_descent(&self) -> (PxPoint, Px) {
3428 self.decoration_line(self.text.underline_descent)
3429 }
3430
3431 pub fn underline_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3437 MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline()))
3438 }
3439
3440 pub fn underline_descent_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3446 MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline_descent()))
3447 }
3448
3449 pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3455 MergingLineIter::new(self.segs().flat_map(move |s| s.underline_skip_glyphs(thickness)))
3456 }
3457
3458 pub fn underline_skip_glyphs_and_spaces(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3464 MergingLineIter::new(
3465 self.segs()
3466 .filter(|s| s.kind().is_word())
3467 .flat_map(move |s| s.underline_skip_glyphs(thickness)),
3468 )
3469 }
3470
3471 fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3472 let r = self.rect();
3473 let y = r.max_y() - bottom_up_offset;
3474 (PxPoint::new(r.origin.x, y), self.width)
3475 }
3476
3477 fn segments(&self) -> &'a [GlyphSegment] {
3478 &self.text.segments.0[self.seg_range.iter()]
3479 }
3480
3481 pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a {
3488 let r = self.glyphs_range();
3489 self.text.glyphs_range(r)
3490 }
3491
3492 pub fn glyphs_with_x_advance(&self) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + 'a)> + 'a {
3494 self.segs().flat_map(|s| s.glyphs_with_x_advance())
3495 }
3496
3497 fn glyphs_range(&self) -> IndexRange {
3498 self.text.segments.glyphs_range(self.seg_range)
3499 }
3500
3501 pub fn segs(&self) -> impl DoubleEndedIterator<Item = ShapedSegment<'a>> + ExactSizeIterator + use<'a> {
3503 let text = self.text;
3504 let line_index = self.index;
3505 self.seg_range.iter().map(move |i| ShapedSegment {
3506 text,
3507 line_index,
3508 index: i,
3509 })
3510 }
3511
3512 pub fn segs_len(&self) -> usize {
3514 self.seg_range.len()
3515 }
3516
3517 pub fn seg(&self, seg_idx: usize) -> Option<ShapedSegment<'_>> {
3521 if self.seg_range.len() > seg_idx {
3522 Some(ShapedSegment {
3523 text: self.text,
3524 line_index: self.index,
3525 index: seg_idx + self.seg_range.start(),
3526 })
3527 } else {
3528 None
3529 }
3530 }
3531
3532 pub fn started_by_wrap(&self) -> bool {
3538 self.index > 0 && {
3539 let prev_line = self.text.lines.segs(self.index - 1);
3540 self.text.segments.0[prev_line.iter()]
3541 .last()
3542 .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3543 .unwrap() }
3545 }
3546
3547 pub fn ended_by_wrap(&self) -> bool {
3553 self.index < self.text.lines.0.len() - 1
3555 && self
3556 .segments()
3557 .last()
3558 .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3559 .unwrap() }
3561
3562 pub fn actual_line_start(&self) -> Self {
3566 let mut r = *self;
3567 while r.started_by_wrap() {
3568 r = r.text.line(r.index - 1).unwrap();
3569 }
3570 r
3571 }
3572
3573 pub fn actual_line_end(&self) -> Self {
3577 let mut r = *self;
3578 while r.ended_by_wrap() {
3579 r = r.text.line(r.index + 1).unwrap();
3580 }
3581 r
3582 }
3583
3584 pub fn text_range(&self) -> ops::Range<usize> {
3586 let start = self.seg_range.start();
3587 let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3588 let end = self.seg_range.end();
3589 let end = if end == 0 { 0 } else { self.text.segments.0[end - 1].text.end };
3590
3591 start..end
3592 }
3593
3594 pub fn text_caret_range(&self) -> ops::Range<usize> {
3599 let start = self.seg_range.start();
3600 let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3601 let end = self.seg_range.end();
3602 let end = if end == 0 {
3603 0
3604 } else if self.seg_range.start() == end {
3605 start
3606 } else {
3607 let seg = &self.text.segments.0[end - 1];
3608 if !matches!(seg.text.kind, TextSegmentKind::LineBreak) {
3609 seg.text.end
3610 } else {
3611 if end == 1 { 0 } else { self.text.segments.0[end - 2].text.end }
3613 }
3614 };
3615
3616 start..end
3617 }
3618
3619 pub fn actual_text_range(&self) -> ops::Range<usize> {
3621 let start = self.actual_line_start().text_range().start;
3622 let end = self.actual_line_end().text_range().end;
3623 start..end
3624 }
3625
3626 pub fn actual_text_caret_range(&self) -> ops::Range<usize> {
3628 let start = self.actual_line_start().text_range().start;
3629 let end = self.actual_line_end().text_caret_range().end;
3630 start..end
3631 }
3632
3633 pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
3637 let r = self.text_range();
3638
3639 let start = r.start.min(full_text.len());
3640 let end = r.end.min(full_text.len());
3641
3642 &full_text[start..end]
3643 }
3644
3645 pub fn nearest_seg(&self, x: Px) -> Option<ShapedSegment<'a>> {
3647 let mut min = None;
3648 let mut min_dist = Px::MAX;
3649 for seg in self.segs() {
3650 let (seg_x, width) = seg.x_width();
3651 if x >= seg_x {
3652 let seg_max_x = seg_x + width;
3653 if x < seg_max_x {
3654 return Some(seg);
3655 }
3656 }
3657 let dist = (x - seg_x).abs();
3658 if min_dist > dist {
3659 min = Some(seg);
3660 min_dist = dist;
3661 }
3662 }
3663 min
3664 }
3665
3666 pub fn index(&self) -> usize {
3668 self.index
3669 }
3670
3671 pub fn directions(&self) -> LayoutDirections {
3673 self.text.lines.0[self.index].directions
3674 }
3675}
3676
3677struct MergingLineIter<I> {
3679 iter: I,
3680 line: Option<(PxPoint, Px)>,
3681}
3682impl<I> MergingLineIter<I> {
3683 pub fn new(iter: I) -> Self {
3684 MergingLineIter { iter, line: None }
3685 }
3686}
3687impl<I: Iterator<Item = (PxPoint, Px)>> Iterator for MergingLineIter<I> {
3688 type Item = I::Item;
3689
3690 fn next(&mut self) -> Option<Self::Item> {
3691 loop {
3692 match self.iter.next() {
3693 Some(line) => {
3694 if let Some(prev_line) = &mut self.line {
3695 fn min_x((origin, _width): (PxPoint, Px)) -> Px {
3696 origin.x
3697 }
3698 fn max_x((origin, width): (PxPoint, Px)) -> Px {
3699 origin.x + width
3700 }
3701
3702 if prev_line.0.y == line.0.y && min_x(*prev_line) <= max_x(line) && max_x(*prev_line) >= min_x(line) {
3703 let x = min_x(*prev_line).min(min_x(line));
3704 prev_line.1 = max_x(*prev_line).max(max_x(line)) - x;
3705 prev_line.0.x = x;
3706 } else {
3707 let cut = mem::replace(prev_line, line);
3708 return Some(cut);
3709 }
3710 } else {
3711 self.line = Some(line);
3712 continue;
3713 }
3714 }
3715 None => return self.line.take(),
3716 }
3717 }
3718 }
3719}
3720
3721struct MergingRectIter<I> {
3722 iter: I,
3723 rect: Option<PxBox>,
3724}
3725impl<I> MergingRectIter<I> {
3726 pub fn new(iter: I) -> Self {
3727 MergingRectIter { iter, rect: None }
3728 }
3729}
3730impl<I: Iterator<Item = PxRect>> Iterator for MergingRectIter<I> {
3731 type Item = I::Item;
3732
3733 fn next(&mut self) -> Option<Self::Item> {
3734 loop {
3735 match self.iter.next() {
3736 Some(rect) => {
3737 let rect = rect.to_box2d();
3738 if let Some(prev_rect) = &mut self.rect {
3739 if prev_rect.min.y == rect.min.y
3740 && prev_rect.max.y == rect.max.y
3741 && prev_rect.min.x <= rect.max.x
3742 && prev_rect.max.x >= rect.min.x
3743 {
3744 prev_rect.min.x = prev_rect.min.x.min(rect.min.x);
3745 prev_rect.max.x = prev_rect.max.x.max(rect.max.x);
3746 continue;
3747 } else {
3748 let cut = mem::replace(prev_rect, rect);
3749 return Some(cut.to_rect());
3750 }
3751 } else {
3752 self.rect = Some(rect);
3753 continue;
3754 }
3755 }
3756 None => return self.rect.take().map(|r| r.to_rect()),
3757 }
3758 }
3759 }
3760}
3761
3762#[derive(Clone, Copy)]
3764pub struct ShapedSegment<'a> {
3765 text: &'a ShapedText,
3766 line_index: usize,
3767 index: usize,
3768}
3769impl fmt::Debug for ShapedSegment<'_> {
3770 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3771 f.debug_struct("ShapedSegment")
3772 .field("line_index", &self.line_index)
3773 .field("index", &self.index)
3774 .finish_non_exhaustive()
3775 }
3776}
3777impl<'a> ShapedSegment<'a> {
3778 pub fn kind(&self) -> TextSegmentKind {
3780 self.text.segments.0[self.index].text.kind
3781 }
3782
3783 pub fn level(&self) -> BidiLevel {
3785 self.text.segments.0[self.index].text.level
3786 }
3787
3788 pub fn direction(&self) -> LayoutDirection {
3790 self.text.segments.0[self.index].text.direction()
3791 }
3792
3793 pub fn has_last_glyph(&self) -> bool {
3795 let seg_glyphs = self.text.segments.glyphs(self.index);
3796 let s = self.text.lines.segs(self.line_index);
3797 let line_glyphs = self.text.segments.glyphs_range(s);
3798 seg_glyphs.end() == line_glyphs.end()
3799 }
3800
3801 fn glyphs_range(&self) -> IndexRange {
3802 self.text.segments.glyphs(self.index)
3803 }
3804
3805 pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> {
3818 let r = self.glyphs_range();
3819 self.text.glyphs_range(r)
3820 }
3821
3822 pub fn glyph(&self, index: usize) -> Option<(&'a Font, GlyphInstance)> {
3824 let mut r = self.glyphs_range();
3825 r.0 += index;
3826 self.text.glyphs_range(r).next().map(|(f, g)| (f, g[0]))
3827 }
3828
3829 pub fn clusters(&self) -> &[u32] {
3837 let r = self.glyphs_range();
3838 self.text.clusters_range(r)
3839 }
3840
3841 pub fn clusters_count(&self) -> usize {
3845 let mut c = u32::MAX;
3846 let mut count = 0;
3847 for &i in self.clusters() {
3848 if i != c {
3849 c = i;
3850 count += 1;
3851 }
3852 }
3853 count
3854 }
3855
3856 pub fn ligature_segs_count(&self) -> usize {
3859 let range = self.glyphs_range();
3860 if range.iter().is_empty() {
3861 0
3862 } else {
3863 self.text.segments.0[self.index + 1..]
3864 .iter()
3865 .filter(|s| s.end == range.end())
3866 .count()
3867 }
3868 }
3869
3870 pub fn glyphs_with_x_advance(
3874 &self,
3875 ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + use<'a>)> + use<'a> {
3876 let r = self.glyphs_range();
3877 self.text.seg_glyphs_with_x_advance(self.index, r)
3878 }
3879
3880 pub fn cluster_glyphs_with_x_advance(
3884 &self,
3885 ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (u32, &'a [GlyphInstance], f32)> + use<'a>)> + use<'a> {
3886 let r = self.glyphs_range();
3887 self.text.seg_cluster_glyphs_with_x_advance(self.index, r)
3888 }
3889
3890 pub fn x_width(&self) -> (Px, Px) {
3892 let IndexRange(start, end) = self.glyphs_range();
3893
3894 let is_line_break = start == end && matches!(self.kind(), TextSegmentKind::LineBreak);
3895
3896 let start_x = match self.direction() {
3897 LayoutDirection::LTR => {
3898 if is_line_break || start == self.text.glyphs.len() {
3899 let x = self.text.lines.x_offset(self.line_index);
3900 let w = self.text.lines.width(self.line_index);
3901 return (Px((x + w) as i32), Px(0));
3902 }
3903 self.text.glyphs[start].point.x
3904 }
3905 LayoutDirection::RTL => {
3906 if is_line_break || start == self.text.glyphs.len() {
3907 let x = self.text.lines.x_offset(self.line_index);
3908 return (Px(x as i32), Px(0));
3909 }
3910
3911 self.text.glyphs[start..end]
3912 .iter()
3913 .map(|g| g.point.x)
3914 .min_by(f32::total_cmp)
3915 .unwrap_or(0.0)
3916 }
3917 };
3918
3919 (Px(start_x.floor() as i32), Px(self.advance().ceil() as i32))
3920 }
3921
3922 pub fn advance(&self) -> f32 {
3924 self.text.segments.0[self.index].advance
3925 }
3926
3927 pub fn rect(&self) -> PxRect {
3929 let (x, width) = self.x_width();
3930 let size = PxSize::new(width, self.text.line_height);
3931
3932 let y = if self.line_index == 0 {
3933 self.text.first_line.origin.y
3934 } else if self.line_index == self.text.lines.0.len() - 1 {
3935 self.text.last_line.origin.y
3936 } else {
3937 self.text.line_height * Px((self.line_index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear
3938 };
3939 PxRect::new(PxPoint::new(x, y), size)
3940 }
3941
3942 pub fn overflow_char_glyph(&self, max_width_px: f32) -> Option<(usize, usize)> {
3944 if self.advance() > max_width_px {
3945 match self.direction() {
3946 LayoutDirection::LTR => {
3947 let mut x = 0.0;
3948 let mut g = 0;
3949 for (_, c) in self.cluster_glyphs_with_x_advance() {
3950 for (cluster, glyphs, advance) in c {
3951 x += advance;
3952 if x > max_width_px {
3953 return Some((cluster as usize, g));
3954 }
3955 g += glyphs.len();
3956 }
3957 }
3958 }
3959 LayoutDirection::RTL => {
3960 let mut g = 0;
3961 let mut rev = smallvec::SmallVec::<[_; 10]>::new();
3962 for (_, c) in self.cluster_glyphs_with_x_advance() {
3963 for (cluster, glyphs, advance) in c {
3964 rev.push((cluster, g, advance));
3965 g += glyphs.len();
3966 }
3967 }
3968
3969 let mut x = 0.0;
3970 for (c, g, advance) in rev.into_iter().rev() {
3971 x += advance;
3972 if x > max_width_px {
3973 return Some((c as usize, g));
3974 }
3975 }
3976 }
3977 }
3978 }
3979 None
3980 }
3981
3982 pub fn inline_info(&self) -> InlineSegmentInfo {
3984 let (x, width) = self.x_width();
3985 InlineSegmentInfo::new(x, width)
3986 }
3987
3988 fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3989 let (x, width) = self.x_width();
3990 let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - bottom_up_offset;
3991 (PxPoint::new(x, y), width)
3992 }
3993
3994 pub fn overline(&self) -> (PxPoint, Px) {
3996 self.decoration_line(self.text.overline)
3997 }
3998
3999 pub fn strikethrough(&self) -> (PxPoint, Px) {
4001 self.decoration_line(self.text.strikethrough)
4002 }
4003
4004 pub fn underline(&self) -> (PxPoint, Px) {
4010 self.decoration_line(self.text.underline)
4011 }
4012
4013 pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + use<'a> {
4017 let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - self.text.underline;
4018 let (x, _) = self.x_width();
4019
4020 let line_y = -(self.text.baseline - self.text.underline).0 as f32;
4021 let line_y_range = (line_y, line_y - thickness.0 as f32);
4022
4023 let padding = (thickness.0 as f32).clamp(1.0, (self.text.fonts.font(0).size().0 as f32 * 0.2).max(1.0));
4025
4026 struct UnderlineSkipGlyphs<'a, I, J> {
4028 line_y_range: (f32, f32),
4029 y: Px,
4030 padding: f32,
4031 min_width: Px,
4032
4033 iter: I,
4034 resume: Option<(&'a Font, J)>,
4035 x: f32,
4036 width: f32,
4037 }
4038 impl<I, J> UnderlineSkipGlyphs<'_, I, J> {
4039 fn line(&self) -> Option<(PxPoint, Px)> {
4040 fn f32_to_px(px: f32) -> Px {
4041 Px(px.round() as i32)
4042 }
4043 let r = (PxPoint::new(f32_to_px(self.x), self.y), f32_to_px(self.width));
4044 if r.1 >= self.min_width { Some(r) } else { None }
4045 }
4046 }
4047 impl<'a, I, J> Iterator for UnderlineSkipGlyphs<'a, I, J>
4048 where
4049 I: Iterator<Item = (&'a Font, J)>,
4050 J: Iterator<Item = (GlyphInstance, f32)>,
4051 {
4052 type Item = (PxPoint, Px);
4053
4054 fn next(&mut self) -> Option<Self::Item> {
4055 loop {
4056 let continuation = self.resume.take().or_else(|| self.iter.next());
4057 if let Some((font, mut glyphs_with_adv)) = continuation {
4058 for (g, a) in &mut glyphs_with_adv {
4059 if let Some((ex_start, ex_end)) = font.h_line_hits(g.index, self.line_y_range) {
4060 self.width += ex_start - self.padding;
4061 let r = self.line();
4062 self.x += self.width + self.padding + ex_end + self.padding;
4063 self.width = a - (ex_start + ex_end) - self.padding;
4064
4065 if r.is_some() {
4066 self.resume = Some((font, glyphs_with_adv));
4067 return r;
4068 }
4069 } else {
4070 self.width += a;
4071 }
4073 }
4074 } else {
4075 let r = self.line();
4076 self.width = 0.0;
4077 return r;
4078 }
4079 }
4080 }
4081 }
4082 UnderlineSkipGlyphs {
4083 line_y_range,
4084 y,
4085 padding,
4086 min_width: Px((padding / 2.0).max(1.0).ceil() as i32),
4087
4088 iter: self.glyphs_with_x_advance(),
4089 resume: None,
4090 x: x.0 as f32,
4091 width: 0.0,
4092 }
4093 }
4094
4095 pub fn underline_descent(&self) -> (PxPoint, Px) {
4101 self.decoration_line(self.text.underline_descent)
4102 }
4103
4104 pub fn text_range(&self) -> ops::Range<usize> {
4106 self.text_start()..self.text_end()
4107 }
4108
4109 pub fn text_start(&self) -> usize {
4111 if self.index == 0 {
4112 0
4113 } else {
4114 self.text.segments.0[self.index - 1].text.end
4115 }
4116 }
4117
4118 pub fn text_end(&self) -> usize {
4120 self.text.segments.0[self.index].text.end
4121 }
4122
4123 pub fn text_glyph_range(&self, glyph_range: impl ops::RangeBounds<usize>) -> ops::Range<usize> {
4127 let included_start = match glyph_range.start_bound() {
4128 ops::Bound::Included(i) => Some(*i),
4129 ops::Bound::Excluded(i) => Some(*i + 1),
4130 ops::Bound::Unbounded => None,
4131 };
4132 let excluded_end = match glyph_range.end_bound() {
4133 ops::Bound::Included(i) => Some(*i - 1),
4134 ops::Bound::Excluded(i) => Some(*i),
4135 ops::Bound::Unbounded => None,
4136 };
4137
4138 let glyph_range_start = self.glyphs_range().start();
4139 let glyph_to_char = |g| self.text.clusters[glyph_range_start + g] as usize;
4140
4141 match (included_start, excluded_end) {
4142 (None, None) => IndexRange(0, self.text_range().len()),
4143 (None, Some(end)) => IndexRange(0, glyph_to_char(end)),
4144 (Some(start), None) => IndexRange(glyph_to_char(start), self.text_range().len()),
4145 (Some(start), Some(end)) => IndexRange(glyph_to_char(start), glyph_to_char(end)),
4146 }
4147 .iter()
4148 }
4149
4150 pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
4154 let r = self.text_range();
4155 let start = r.start.min(full_text.len());
4156 let end = r.end.min(full_text.len());
4157 &full_text[start..end]
4158 }
4159
4160 pub fn nearest_char_index(&self, x: Px, full_text: &str) -> usize {
4162 let txt_range = self.text_range();
4163 let is_rtl = self.direction().is_rtl();
4164 let x = x.0 as f32;
4165
4166 let seg_clusters = self.clusters();
4167
4168 for (font, clusters) in self.cluster_glyphs_with_x_advance() {
4169 for (cluster, glyphs, advance) in clusters {
4170 let found = x < glyphs[0].point.x || glyphs[0].point.x + advance > x;
4171 if !found {
4172 continue;
4173 }
4174 let cluster_i = seg_clusters.iter().position(|&c| c == cluster).unwrap();
4175
4176 let char_a = txt_range.start + cluster as usize;
4177 let char_b = if is_rtl {
4178 if cluster_i == 0 {
4179 txt_range.end
4180 } else {
4181 txt_range.start + seg_clusters[cluster_i - 1] as usize
4182 }
4183 } else {
4184 let next_cluster = cluster_i + glyphs.len();
4185 if next_cluster == seg_clusters.len() {
4186 txt_range.end
4187 } else {
4188 txt_range.start + seg_clusters[next_cluster] as usize
4189 }
4190 };
4191
4192 if char_b - char_a > 1 && glyphs.len() == 1 {
4193 let text = &full_text[char_a..char_b];
4196
4197 let mut lig_parts = smallvec::SmallVec::<[u16; 6]>::new_const();
4198 for (i, _) in unicode_segmentation::UnicodeSegmentation::grapheme_indices(text, true) {
4199 lig_parts.push(i as u16);
4200 }
4201
4202 if lig_parts.len() > 1 {
4203 let x = x - glyphs[0].point.x;
4206
4207 let mut split = true;
4208 for (i, font_caret) in font.ligature_caret_offsets(glyphs[0].index).enumerate() {
4209 if i == lig_parts.len() {
4210 break;
4211 }
4212 split = false;
4213
4214 if font_caret > x {
4215 return char_a + lig_parts[i] as usize;
4217 }
4218 }
4219 if split {
4220 let lig_part = advance / lig_parts.len() as f32;
4222 let mut lig_x = lig_part;
4223 if is_rtl {
4224 for c in lig_parts.into_iter().rev() {
4225 if lig_x > x {
4226 return char_a + c as usize;
4228 }
4229 lig_x += lig_part;
4230 }
4231 } else {
4232 for c in lig_parts {
4233 if lig_x > x {
4234 return char_a + c as usize;
4235 }
4236 lig_x += lig_part;
4237 }
4238 }
4239 }
4240 }
4241 }
4242 let middle_x = glyphs[0].point.x + advance / 2.0;
4245
4246 return if is_rtl {
4247 if x <= middle_x { char_b } else { char_a }
4248 } else if x <= middle_x {
4249 char_a
4250 } else {
4251 char_b
4252 };
4253 }
4254 }
4255
4256 let mut start = is_rtl;
4257 if matches!(self.kind(), TextSegmentKind::LineBreak) {
4258 start = !start;
4259 }
4260 if start { txt_range.start } else { txt_range.end }
4261 }
4262
4263 pub fn index(&self) -> usize {
4265 self.index - self.text.lines.segs(self.line_index).start()
4266 }
4267}
4268
4269const WORD_CACHE_MAX_LEN: usize = 32;
4270const WORD_CACHE_MAX_ENTRIES: usize = 10_000;
4271
4272#[derive(Hash, PartialEq, Eq)]
4273pub(super) struct WordCacheKey<S> {
4274 string: S,
4275 ctx_key: WordContextKey,
4276}
4277#[derive(Hash)]
4278struct WordCacheKeyRef<'a, S> {
4279 string: &'a S,
4280 ctx_key: &'a WordContextKey,
4281}
4282
4283#[derive(Hash, PartialEq, Eq, Clone)]
4284pub(super) struct WordContextKey {
4285 lang: unic_langid::subtags::Language,
4286 script: Option<unic_langid::subtags::Script>,
4287 direction: LayoutDirection,
4288 features: Box<[usize]>,
4289}
4290impl WordContextKey {
4291 pub fn new(lang: &Lang, direction: LayoutDirection, font_features: &RFontFeatures) -> Self {
4292 let is_64 = mem::size_of::<usize>() == mem::size_of::<u64>();
4293
4294 let mut features = vec![];
4295
4296 if !font_features.is_empty() {
4297 features.reserve(font_features.len() * if is_64 { 3 } else { 4 });
4298 for feature in font_features {
4299 if is_64 {
4300 let mut h = feature.tag.0 as u64;
4301 h |= (feature.value as u64) << 32;
4302 features.push(h as usize);
4303 } else {
4304 features.push(feature.tag.0 as usize);
4305 features.push(feature.value as usize);
4306 }
4307
4308 features.push(feature.start as usize);
4309 features.push(feature.end as usize);
4310 }
4311 }
4312
4313 WordContextKey {
4314 lang: lang.language,
4315 script: lang.script,
4316 direction,
4317 features: features.into_boxed_slice(),
4318 }
4319 }
4320
4321 pub fn harfbuzz_lang(&self) -> Option<rustybuzz::Language> {
4322 self.lang.as_str().parse().ok()
4323 }
4324
4325 pub fn harfbuzz_script(&self) -> Option<rustybuzz::Script> {
4326 let t: u32 = self.script?.into();
4327 let t = t.to_le_bytes(); rustybuzz::Script::from_iso15924_tag(ttf_parser::Tag::from_bytes(&[t[0], t[1], t[2], t[3]]))
4329 }
4330
4331 pub fn harfbuzz_direction(&self) -> rustybuzz::Direction {
4332 into_harf_direction(self.direction)
4333 }
4334}
4335
4336#[derive(Debug, Clone, Default)]
4337pub(super) struct ShapedSegmentData {
4338 glyphs: Vec<ShapedGlyph>,
4339 x_advance: f32,
4340 y_advance: f32,
4341}
4342#[derive(Debug, Clone, Copy)]
4343struct ShapedGlyph {
4344 index: u32,
4346 cluster: u32,
4348 point: (f32, f32),
4349}
4350
4351impl Font {
4352 fn buffer_segment(&self, segment: &str, key: &WordContextKey) -> rustybuzz::UnicodeBuffer {
4353 let mut buffer = rustybuzz::UnicodeBuffer::new();
4354 buffer.set_direction(key.harfbuzz_direction());
4355 buffer.set_cluster_level(rustybuzz::BufferClusterLevel::MonotoneCharacters);
4356
4357 if let Some(lang) = key.harfbuzz_lang() {
4358 buffer.set_language(lang);
4359 }
4360 if let Some(script) = key.harfbuzz_script() {
4361 buffer.set_script(script);
4362 }
4363
4364 buffer.push_str(segment);
4365 buffer
4366 }
4367
4368 fn shape_segment_no_cache(&self, seg: &str, key: &WordContextKey, features: &[rustybuzz::Feature]) -> ShapedSegmentData {
4369 let buffer = if let Some(font) = self.harfbuzz() {
4370 let buffer = self.buffer_segment(seg, key);
4371 rustybuzz::shape(&font, features, buffer)
4372 } else {
4373 return ShapedSegmentData {
4374 glyphs: vec![],
4375 x_advance: 0.0,
4376 y_advance: 0.0,
4377 };
4378 };
4379
4380 let size_scale = self.metrics().size_scale;
4381 let to_layout = |p: i32| p as f32 * size_scale;
4382
4383 let mut w_x_advance = 0.0;
4384 let mut w_y_advance = 0.0;
4385
4386 let glyphs: Vec<_> = buffer
4387 .glyph_infos()
4388 .iter()
4389 .zip(buffer.glyph_positions())
4390 .map(|(i, p)| {
4391 let x_offset = to_layout(p.x_offset);
4392 let y_offset = -to_layout(p.y_offset);
4393 let x_advance = to_layout(p.x_advance);
4394 let y_advance = to_layout(p.y_advance);
4395
4396 let point = (w_x_advance + x_offset, w_y_advance + y_offset);
4397 w_x_advance += x_advance;
4398 w_y_advance += y_advance;
4399
4400 ShapedGlyph {
4401 index: i.glyph_id,
4402 cluster: i.cluster,
4403 point,
4404 }
4405 })
4406 .collect();
4407
4408 ShapedSegmentData {
4409 glyphs,
4410 x_advance: w_x_advance,
4411 y_advance: w_y_advance,
4412 }
4413 }
4414
4415 fn shape_segment<R>(
4416 &self,
4417 seg: &str,
4418 word_ctx_key: &WordContextKey,
4419 features: &[rustybuzz::Feature],
4420 out: impl FnOnce(&ShapedSegmentData) -> R,
4421 ) -> R {
4422 if !(1..=WORD_CACHE_MAX_LEN).contains(&seg.len()) || self.face().is_empty() {
4423 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4424 out(&seg)
4425 } else if let Some(small) = Self::to_small_word(seg) {
4426 let cache = self.0.small_word_cache.read();
4428
4429 let hash = cache.hasher().hash_one(WordCacheKeyRef {
4430 string: &small,
4431 ctx_key: word_ctx_key,
4432 });
4433
4434 if let Some((_, seg)) = cache
4435 .raw_entry()
4436 .from_hash(hash, |e| e.string == small && &e.ctx_key == word_ctx_key)
4437 {
4438 return out(seg);
4439 }
4440 drop(cache);
4441
4442 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4444 let key = WordCacheKey {
4445 string: small,
4446 ctx_key: word_ctx_key.clone(),
4447 };
4448 let r = out(&seg);
4449 let mut cache = self.0.small_word_cache.write();
4450 if cache.len() > WORD_CACHE_MAX_ENTRIES {
4451 cache.clear();
4452 }
4453 cache.insert(key, seg);
4454 r
4455 } else {
4456 let cache = self.0.word_cache.read();
4458
4459 let hash = cache.hasher().hash_one(WordCacheKeyRef {
4460 string: &seg,
4461 ctx_key: word_ctx_key,
4462 });
4463
4464 if let Some((_, seg)) = cache
4465 .raw_entry()
4466 .from_hash(hash, |e| e.string.as_str() == seg && &e.ctx_key == word_ctx_key)
4467 {
4468 return out(seg);
4469 }
4470 drop(cache);
4471
4472 let string = seg.to_owned();
4474 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4475 let key = WordCacheKey {
4476 string,
4477 ctx_key: word_ctx_key.clone(),
4478 };
4479 let r = out(&seg);
4480 let mut cache = self.0.word_cache.write();
4481 if cache.len() > WORD_CACHE_MAX_ENTRIES {
4482 cache.clear();
4483 }
4484 cache.insert(key, seg);
4485 r
4486 }
4487 }
4488
4489 pub fn space_index(&self) -> GlyphIndex {
4491 self.shape_space().0
4492 }
4493
4494 pub fn space_x_advance(&self) -> Px {
4496 self.shape_space().1
4497 }
4498
4499 fn shape_space(&self) -> (GlyphIndex, Px) {
4500 let mut id = 0;
4501 let mut adv = 0.0;
4502 self.shape_segment(
4503 " ",
4504 &WordContextKey {
4505 lang: unic_langid::subtags::Language::from_bytes(b"und").unwrap(),
4506 script: None,
4507 direction: LayoutDirection::LTR,
4508 features: Box::new([]),
4509 },
4510 &[],
4511 |r| {
4512 id = r.glyphs.last().map(|g| g.index).unwrap_or(0);
4513 adv = r.x_advance;
4514 },
4515 );
4516 (id, Px(adv as _))
4517 }
4518
4519 pub fn shape_text(self: &Font, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4521 ShapedTextBuilder::shape_text(std::slice::from_ref(self), text, config)
4522 }
4523
4524 pub fn outline(&self, glyph_id: GlyphIndex, sink: &mut impl OutlineSink) -> Option<PxRect> {
4528 struct AdapterSink<'a, S> {
4529 sink: &'a mut S,
4530 scale: f32,
4531 }
4532 impl<S: OutlineSink> ttf_parser::OutlineBuilder for AdapterSink<'_, S> {
4533 fn move_to(&mut self, x: f32, y: f32) {
4534 self.sink.move_to(euclid::point2(x, y) * self.scale)
4535 }
4536
4537 fn line_to(&mut self, x: f32, y: f32) {
4538 self.sink.line_to(euclid::point2(x, y) * self.scale)
4539 }
4540
4541 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
4542 let ctrl = euclid::point2(x1, y1) * self.scale;
4543 let to = euclid::point2(x, y) * self.scale;
4544 self.sink.quadratic_curve_to(ctrl, to)
4545 }
4546
4547 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
4548 let l_from = euclid::point2(x1, y1) * self.scale;
4549 let l_to = euclid::point2(x2, y2) * self.scale;
4550 let to = euclid::point2(x, y) * self.scale;
4551 self.sink.cubic_curve_to((l_from, l_to), to)
4552 }
4553
4554 fn close(&mut self) {
4555 self.sink.close()
4556 }
4557 }
4558
4559 let scale = self.metrics().size_scale;
4560
4561 let f = self.harfbuzz()?;
4562 let r = f.outline_glyph(ttf_parser::GlyphId(glyph_id as _), &mut AdapterSink { sink, scale })?;
4563 Some(
4564 PxRect::new(
4565 PxPoint::new(Px(r.x_min as _), Px(r.y_min as _)),
4566 PxSize::new(Px(r.width() as _), Px(r.height() as _)),
4567 ) * Factor2d::uniform(scale),
4568 )
4569 }
4570
4571 pub fn h_line_hits(&self, glyph_id: GlyphIndex, line_y_range: (f32, f32)) -> Option<(f32, f32)> {
4581 struct InterceptsSink {
4587 start: Option<euclid::Point2D<f32, Px>>,
4588 current: euclid::Point2D<f32, Px>,
4589 under: (bool, bool),
4590
4591 line_y_range: (f32, f32),
4592 hit: Option<(f32, f32)>,
4593 }
4594 impl OutlineSink for InterceptsSink {
4595 fn move_to(&mut self, to: euclid::Point2D<f32, Px>) {
4596 self.start = Some(to);
4597 self.current = to;
4598 self.under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4599 }
4600
4601 fn line_to(&mut self, to: euclid::Point2D<f32, Px>) {
4602 let under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4603
4604 if self.under != under || under == (true, false) {
4605 self.under = under;
4607
4608 let (x0, x1) = if self.current.x < to.x {
4609 (self.current.x, to.x)
4610 } else {
4611 (to.x, self.current.x)
4612 };
4613 if let Some((min, max)) = &mut self.hit {
4614 *min = min.min(x0);
4615 *max = max.max(x1);
4616 } else {
4617 self.hit = Some((x0, x1));
4618 }
4619 }
4620
4621 self.current = to;
4622 self.under = under;
4623 }
4624
4625 fn quadratic_curve_to(&mut self, _: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>) {
4626 self.line_to(to);
4627 }
4628
4629 fn cubic_curve_to(&mut self, _: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>) {
4630 self.line_to(to);
4631 }
4632
4633 fn close(&mut self) {
4634 if let Some(s) = self.start.take()
4635 && s != self.current
4636 {
4637 self.line_to(s);
4638 }
4639 }
4640 }
4641 let mut sink = InterceptsSink {
4642 start: None,
4643 current: euclid::point2(0.0, 0.0),
4644 under: (false, false),
4645
4646 line_y_range,
4647 hit: None,
4648 };
4649 self.outline(glyph_id, &mut sink)?;
4650
4651 sink.hit.map(|(a, b)| (a, b - a))
4652 }
4653}
4654
4655pub trait OutlineSink {
4659 fn move_to(&mut self, to: euclid::Point2D<f32, Px>);
4661 fn line_to(&mut self, to: euclid::Point2D<f32, Px>);
4663 fn quadratic_curve_to(&mut self, ctrl: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>);
4665 fn cubic_curve_to(&mut self, ctrl: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>);
4669 fn close(&mut self);
4671}
4672
4673impl FontList {
4674 pub fn shape_text(&self, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4676 ShapedTextBuilder::shape_text(self, text, config)
4677 }
4678}
4679
4680#[derive(Clone, Copy)]
4682struct IndexRange(pub usize, pub usize);
4683impl fmt::Debug for IndexRange {
4684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4685 write!(f, "{}..{}", self.0, self.1)
4686 }
4687}
4688impl IntoIterator for IndexRange {
4689 type Item = usize;
4690
4691 type IntoIter = std::ops::Range<usize>;
4692
4693 fn into_iter(self) -> Self::IntoIter {
4694 self.iter()
4695 }
4696}
4697impl From<IndexRange> for std::ops::Range<usize> {
4698 fn from(c: IndexRange) -> Self {
4699 c.iter()
4700 }
4701}
4702impl From<std::ops::Range<usize>> for IndexRange {
4703 fn from(r: std::ops::Range<usize>) -> Self {
4704 IndexRange(r.start, r.end)
4705 }
4706}
4707impl IndexRange {
4708 pub fn from_bounds(bounds: impl ops::RangeBounds<usize>) -> Self {
4709 let start = match bounds.start_bound() {
4711 ops::Bound::Included(&i) => i,
4712 ops::Bound::Excluded(&i) => i + 1,
4713 ops::Bound::Unbounded => 0,
4714 };
4715 let end = match bounds.end_bound() {
4716 ops::Bound::Included(&i) => i + 1,
4717 ops::Bound::Excluded(&i) => i,
4718 ops::Bound::Unbounded => 0,
4719 };
4720 Self(start, end)
4721 }
4722
4723 pub fn iter(self) -> std::ops::Range<usize> {
4725 self.0..self.1
4726 }
4727
4728 pub fn start(self) -> usize {
4730 self.0
4731 }
4732
4733 pub fn end(self) -> usize {
4735 self.1
4736 }
4737
4738 pub fn len(self) -> usize {
4740 self.end() - self.start()
4741 }
4742}
4743impl std::ops::RangeBounds<usize> for IndexRange {
4744 fn start_bound(&self) -> std::ops::Bound<&usize> {
4745 std::ops::Bound::Included(&self.0)
4746 }
4747
4748 fn end_bound(&self) -> std::ops::Bound<&usize> {
4749 std::ops::Bound::Excluded(&self.1)
4750 }
4751}
4752
4753pub fn f32_cmp(a: &f32, b: &f32) -> std::cmp::Ordering {
4755 a.partial_cmp(b).unwrap()
4756}
4757
4758fn into_harf_direction(d: LayoutDirection) -> rustybuzz::Direction {
4759 match d {
4760 LayoutDirection::LTR => rustybuzz::Direction::LeftToRight,
4761 LayoutDirection::RTL => rustybuzz::Direction::RightToLeft,
4762 }
4763}
4764
4765#[cfg(test)]
4766mod tests {
4767 use crate::{
4768 FONTS, Font, FontManager, FontName, FontStretch, FontStyle, FontWeight, ParagraphBreak, SegmentedText, TextReshapingArgs,
4769 TextShapingArgs, WordContextKey,
4770 };
4771 use zng_app::APP;
4772 use zng_ext_l10n::lang;
4773 use zng_layout::{
4774 context::LayoutDirection,
4775 unit::{Px, PxConstraints2d, TimeUnits},
4776 };
4777
4778 fn test_font() -> Font {
4779 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
4780 let font = app
4781 .block_on_fut(
4782 async {
4783 FONTS
4784 .normal(&FontName::sans_serif(), &lang!(und))
4785 .wait_rsp()
4786 .await
4787 .unwrap()
4788 .sized(Px(20), vec![])
4789 },
4790 60.secs(),
4791 )
4792 .unwrap();
4793 drop(app);
4794 font
4795 }
4796
4797 #[test]
4798 fn set_line_spacing() {
4799 let text = "0\n1\n2\n3\n4";
4800 test_line_spacing(text, Px(20), Px(0));
4801 test_line_spacing(text, Px(0), Px(20));
4802 test_line_spacing(text, Px(4), Px(6));
4803 test_line_spacing(text, Px(4), Px(4));
4804 test_line_spacing("a line\nanother\nand another", Px(20), Px(0));
4805 test_line_spacing("", Px(20), Px(0));
4806 test_line_spacing("a line", Px(20), Px(0));
4807 }
4808 fn test_line_spacing(text: &'static str, from: Px, to: Px) {
4809 let font = test_font();
4810 let mut config = TextShapingArgs {
4811 line_height: Px(40),
4812 line_spacing: from,
4813 ..Default::default()
4814 };
4815
4816 let text = SegmentedText::new(text, LayoutDirection::LTR);
4817 let mut test = font.shape_text(&text, &config);
4818
4819 config.line_spacing = to;
4820 let expected = font.shape_text(&text, &config);
4821
4822 assert_eq!(from, test.line_spacing());
4823 test.reshape_lines2(&TextReshapingArgs {
4824 constraints: PxConstraints2d::new_fill_size(test.align_size()),
4825 inline_constraints: None,
4826 align: test.align(),
4827 overflow_align: test.overflow_align(),
4828 direction: test.direction(),
4829 line_height: test.line_height(),
4830 line_spacing: to,
4831 paragraph_spacing: Px(0),
4832 paragraph_indent: (Px(0), false),
4833 paragraph_break: ParagraphBreak::None,
4834 });
4835 assert_eq!(to, test.line_spacing());
4836
4837 for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4838 assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4839 }
4840
4841 assert_eq!(test.size(), expected.size());
4842 }
4843
4844 #[test]
4845 fn set_line_height() {
4846 let text = "0\n1\n2\n3\n4";
4847 test_line_height(text, Px(20), Px(20));
4848 test_line_height(text, Px(20), Px(10));
4849 test_line_height(text, Px(10), Px(20));
4850 test_line_height("a line\nanother\nand another", Px(20), Px(10));
4851 test_line_height("", Px(20), Px(10));
4852 test_line_height("a line", Px(20), Px(10));
4853 }
4854 fn test_line_height(text: &'static str, from: Px, to: Px) {
4855 let font = test_font();
4856 let mut config = TextShapingArgs {
4857 line_height: from,
4858 line_spacing: Px(20),
4859 ..Default::default()
4860 };
4861
4862 let text = SegmentedText::new(text, LayoutDirection::LTR);
4863 let mut test = font.shape_text(&text, &config);
4864
4865 config.line_height = to;
4866 let expected = font.shape_text(&text, &config);
4867
4868 assert_eq!(from, test.line_height());
4869 test.reshape_lines2(&TextReshapingArgs {
4870 constraints: PxConstraints2d::new_fill_size(test.align_size()),
4871 inline_constraints: None,
4872 align: test.align(),
4873 overflow_align: test.overflow_align(),
4874 direction: test.direction(),
4875 line_height: to,
4876 line_spacing: test.line_spacing(),
4877 paragraph_spacing: Px(0),
4878 paragraph_indent: (Px(0), false),
4879 paragraph_break: ParagraphBreak::None,
4880 });
4881 assert_eq!(to, test.line_height());
4882
4883 for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4884 assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4885 }
4886
4887 assert_eq!(test.size(), expected.size());
4888 }
4889
4890 #[test]
4891 fn font_fallback_issue() {
4892 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
4893 app.block_on_fut(
4894 async {
4895 let font = FONTS
4896 .list(
4897 &[FontName::new("Consolas"), FontName::monospace()],
4898 FontStyle::Normal,
4899 FontWeight::NORMAL,
4900 FontStretch::NORMAL,
4901 &lang!(und),
4902 )
4903 .wait_rsp()
4904 .await
4905 .sized(Px(20), vec![]);
4906
4907 let config = TextShapingArgs::default();
4908
4909 let txt_seg = SegmentedText::new("النص ثنائي الاتجاه (بالإنجليزية:Bi", LayoutDirection::RTL);
4910 let txt_shape = font.shape_text(&txt_seg, &config);
4911
4912 let _ok = (txt_seg, txt_shape);
4913 },
4914 60.secs(),
4915 )
4916 .unwrap()
4917 }
4918
4919 #[test]
4920 fn cluster_is_byte() {
4921 let font = test_font();
4922
4923 let data = font.shape_segment_no_cache("£a", &WordContextKey::new(&lang!("en-US"), LayoutDirection::LTR, &vec![]), &[]);
4924
4925 for ((i, _), g) in "£a".char_indices().zip(&data.glyphs) {
4926 assert_eq!(i as u32, g.cluster);
4927 }
4928 }
4929}