1use std::{
2 cmp, fmt,
3 hash::{BuildHasher, Hash},
4 mem, ops,
5};
6
7use skrifa::MetadataProvider;
8use zng_app::widget::info::InlineSegmentInfo;
9use zng_ext_image::{ColorType, IMAGES, ImageDataFormat, ImageOptions, ImageSource, ImageVar};
10use zng_ext_l10n::{Lang, lang};
11use zng_layout::{
12 context::{InlineConstraintsLayout, InlineConstraintsMeasure, InlineSegmentPos, LayoutDirection, TextSegmentKind},
13 unit::{Align, FactorUnits, Px, PxBox, PxConstraints2d, PxPoint, PxRect, PxSize, about_eq, euclid},
14};
15use zng_txt::Txt;
16use zng_view_api::font::{GlyphIndex, GlyphInstance};
17
18use crate::{
19 BidiLevel, CaretIndex, Font, FontList, HYPHENATION, Hyphens, Justify, LineBreak, ParagraphBreak, SegmentedText, TextSegment, WordBreak,
20 font_features::RFontFeatures,
21};
22
23#[derive(Clone, Copy, PartialEq, Debug)]
25#[non_exhaustive]
26pub enum GlyphLoadingError {
27 NoSuchGlyph,
29 PlatformError,
31}
32impl std::error::Error for GlyphLoadingError {}
33impl fmt::Display for GlyphLoadingError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 use GlyphLoadingError::*;
36 match self {
37 NoSuchGlyph => write!(f, "no such glyph"),
38 PlatformError => write!(f, "platform error"),
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45#[non_exhaustive]
46pub struct TextShapingArgs {
47 pub letter_spacing: Px,
49
50 pub word_spacing: Px,
52
53 pub line_height: Px,
59
60 pub line_spacing: Px,
62
63 pub paragraph_spacing: Px,
65
66 pub paragraph_indent: (Px, bool),
70
71 pub lang: Lang,
73
74 pub direction: LayoutDirection,
76
77 pub ignore_ligatures: bool,
79
80 pub disable_kerning: bool,
82
83 pub tab_x_advance: Px,
85
86 pub inline_constraints: Option<InlineConstraintsMeasure>,
88
89 pub font_features: RFontFeatures,
91
92 pub max_width: Px,
96
97 pub paragraph_break: ParagraphBreak,
99
100 pub line_break: LineBreak,
102
103 pub word_break: WordBreak,
107
108 pub hyphens: Hyphens,
110
111 pub hyphen_char: Txt,
113
114 pub obscuring_char: Option<char>,
116}
117impl Default for TextShapingArgs {
118 fn default() -> Self {
119 TextShapingArgs {
120 letter_spacing: Px(0),
121 word_spacing: Px(0),
122 line_height: Px(0),
123 line_spacing: Px(0),
124 paragraph_spacing: Px(0),
125 paragraph_indent: (Px(0), false),
126 lang: lang!(und),
127 direction: LayoutDirection::LTR,
128 ignore_ligatures: false,
129 disable_kerning: false,
130 tab_x_advance: Px(0),
131 inline_constraints: None,
132 font_features: RFontFeatures::default(),
133 max_width: Px::MAX,
134 paragraph_break: Default::default(),
135 line_break: Default::default(),
136 word_break: Default::default(),
137 hyphens: Default::default(),
138 hyphen_char: Txt::from_char('-'),
139 obscuring_char: None,
140 }
141 }
142}
143
144#[derive(Debug, Clone, Default)]
146#[non_exhaustive]
147pub struct TextReshapingArgs {
148 pub constraints: PxConstraints2d,
150 pub inline_constraints: Option<InlineConstraintsLayout>,
152 pub align: Align,
154 pub overflow_align: Align,
156 pub direction: LayoutDirection,
158
159 pub line_height: Px,
161 pub line_spacing: Px,
163 pub paragraph_spacing: Px,
165 pub paragraph_indent: (Px, bool),
169 pub paragraph_break: ParagraphBreak,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq)]
175struct LineRange {
176 end: usize,
179 width: f32,
181 x_offset: f32,
183 directions: LayoutDirections,
184}
185
186#[derive(Clone)]
188struct FontRange {
189 font: Font,
190 end: usize,
192}
193impl PartialEq for FontRange {
194 fn eq(&self, other: &Self) -> bool {
195 self.font == other.font && self.end == other.end
196 }
197}
198impl fmt::Debug for FontRange {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 f.debug_struct("FontInfo")
201 .field("font", &self.font.face().display_name().name())
202 .field("end", &self.end)
203 .finish()
204 }
205}
206
207#[derive(Debug, Clone, Copy, PartialEq)]
208struct GlyphSegment {
209 pub text: TextSegment,
210 pub end: usize,
212 pub x: f32,
214 pub advance: f32,
216}
217
218#[derive(Debug, Default, Clone, PartialEq)]
220struct GlyphSegmentVec(Vec<GlyphSegment>);
221impl GlyphSegmentVec {
222 fn glyphs(&self, index: usize) -> IndexRange {
224 let start = if index == 0 { 0 } else { self.0[index - 1].end };
225 let end = self.0[index].end;
226 IndexRange(start, end)
227 }
228
229 fn glyphs_range(&self, range: IndexRange) -> IndexRange {
231 let IndexRange(start, end) = range;
232
233 if end == 0 {
234 return IndexRange(0, 0);
235 }
236
237 let start = if start == 0 { 0 } else { self.0[start - 1].end };
238 let end = self.0[end - 1].end;
239
240 IndexRange(start, end)
241 }
242}
243
244#[derive(Debug, Default, Clone, PartialEq)]
246struct LineRangeVec(Vec<LineRange>);
247impl LineRangeVec {
248 fn segs(&self, index: usize) -> IndexRange {
250 let end = self.0[index].end;
251 let start = if index == 0 { 0 } else { self.0[index - 1].end };
252 IndexRange(start, end)
253 }
254
255 fn width(&self, index: usize) -> f32 {
257 self.0[index].width
258 }
259
260 fn x_offset(&self, index: usize) -> f32 {
262 self.0[index].x_offset
263 }
264
265 fn iter_segs(&self) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
267 self.iter_segs_skip(0)
268 }
269
270 fn iter_segs_skip(&self, start_line: usize) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
272 let mut start = self.segs(start_line).start();
273 self.0[start_line..].iter().map(move |l| {
274 let r = IndexRange(start, l.end);
275 start = l.end;
276 (l.width, r)
277 })
278 }
279
280 fn is_multi(&self) -> bool {
282 self.0.len() > 1
283 }
284
285 fn first_mut(&mut self) -> &mut LineRange {
286 &mut self.0[0]
287 }
288
289 fn last(&self) -> LineRange {
290 self.0[self.0.len() - 1]
291 }
292
293 fn last_mut(&mut self) -> &mut LineRange {
294 let l = self.0.len() - 1;
295 &mut self.0[l]
296 }
297}
298
299#[derive(Debug, Default, Clone, PartialEq)]
301struct FontRangeVec(Vec<FontRange>);
302impl FontRangeVec {
303 fn iter_glyphs(&self) -> impl Iterator<Item = (&Font, IndexRange)> + '_ {
305 let mut start = 0;
306 self.0.iter().map(move |f| {
307 let r = IndexRange(start, f.end);
308 start = f.end;
309 (&f.font, r)
310 })
311 }
312
313 fn iter_glyphs_clip(&self, glyphs_range: IndexRange) -> impl Iterator<Item = (&Font, IndexRange)> + '_ {
315 let mut start = glyphs_range.start();
316 let end = glyphs_range.end();
317 let first_font = self.0.iter().position(|f| f.end > start).unwrap_or(self.0.len().saturating_sub(1));
318
319 self.0[first_font..].iter().map_while(move |f| {
320 let i = f.end.min(end);
321
322 if i > start {
323 let r = IndexRange(start, i);
324 start = i;
325 Some((&f.font, r))
326 } else {
327 None
328 }
329 })
330 }
331
332 fn font(&self, index: usize) -> &Font {
334 &self.0[index].font
335 }
336}
337
338#[derive(Clone)]
339struct GlyphImage(ImageVar);
340impl PartialEq for GlyphImage {
341 fn eq(&self, other: &Self) -> bool {
342 self.0.var_eq(&other.0)
343 }
344}
345impl fmt::Debug for GlyphImage {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 write!(f, "GlyphImage(_)")
348 }
349}
350
351#[derive(Debug, Clone, PartialEq)]
355pub struct ShapedText {
356 glyphs: Vec<GlyphInstance>,
358 clusters: Vec<u32>,
360 segments: GlyphSegmentVec,
362 lines: LineRangeVec,
363 fonts: FontRangeVec,
364 images: Vec<(u32, GlyphImage)>,
366
367 line_height: Px,
368 line_spacing: Px,
369 paragraph_spacing: Px,
370 paragraph_break: ParagraphBreak,
371
372 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 pub fn reshape_lines(&mut self, args: &TextReshapingArgs) {
769 self.clear_justify_impl(args.align.is_fill_x());
770 self.reshape_line_height_and_spacing(args.line_height, args.line_spacing);
771
772 let is_inlined = args.inline_constraints.is_some();
773
774 let align_x = args.align.x(args.direction);
775 let align_y = if is_inlined { 0.fct() } else { args.align.y() };
776 let overflow_align_x = args.overflow_align.x(args.direction);
777 let overflow_align_y = if is_inlined { 0.fct() } else { args.overflow_align.y() };
778
779 let (first, mid, last, first_segs, last_segs) = if let Some(l) = &args.inline_constraints {
780 (l.first, l.mid_clear, l.last, &*l.first_segs, &*l.last_segs)
781 } else {
782 let block_size = self.block_size();
784 let align_size = args.constraints.fill_size_or(block_size);
785
786 let mut first = PxRect::from_size(self.line(0).map(|l| l.rect().size).unwrap_or_default());
787 let mut last = PxRect::from_size(
788 self.line(self.lines_len().saturating_sub(1))
789 .map(|l| l.rect().size)
790 .unwrap_or_default(),
791 );
792 last.origin.y = block_size.height - last.size.height;
793
794 match first.size.width.cmp(&align_size.width) {
795 cmp::Ordering::Less => first.origin.x = (align_size.width - first.size.width) * align_x,
796 cmp::Ordering::Equal => {}
797 cmp::Ordering::Greater => first.origin.x = (align_size.width - first.size.width) * overflow_align_x,
798 }
799 match last.size.width.cmp(&align_size.width) {
800 cmp::Ordering::Less => last.origin.x = (align_size.width - last.size.width) * align_x,
801 cmp::Ordering::Equal => {}
802 cmp::Ordering::Greater => last.origin.x = (align_size.width - last.size.width) * overflow_align_x,
803 }
804
805 match block_size.height.cmp(&align_size.height) {
806 cmp::Ordering::Less => {
807 let align_y = (align_size.height - block_size.height) * align_y;
808 first.origin.y += align_y;
809 last.origin.y += align_y;
810 }
811 cmp::Ordering::Equal => {}
812 cmp::Ordering::Greater => {
813 let align_y = (align_size.height - block_size.height) * overflow_align_y;
814 first.origin.y += align_y;
815 last.origin.y += align_y;
816 }
817 }
818
819 static EMPTY: Vec<InlineSegmentPos> = vec![];
820 (first, Px(0), last, &EMPTY, &EMPTY)
821 };
822
823 if !self.lines.0.is_empty() {
824 if self.first_line != first {
825 let first_offset = (first.origin - self.first_line.origin).cast::<f32>().cast_unit();
826
827 let first_range = self.lines.segs(0);
828 let first_glyphs = self.segments.glyphs_range(first_range);
829
830 for g in &mut self.glyphs[first_glyphs.iter()] {
831 g.point += first_offset;
832 }
833
834 let first_line = self.lines.first_mut();
835 first_line.x_offset = first.origin.x.0 as f32;
836 }
840 if !first_segs.is_empty() {
841 let first_range = self.lines.segs(0);
843 if first_range.len() == first_segs.len() {
844 for i in first_range.iter() {
845 let seg_offset = first_segs[i].x - self.segments.0[i].x;
846 let glyphs = self.segments.glyphs(i);
847 for g in &mut self.glyphs[glyphs.iter()] {
848 g.point.x += seg_offset;
849 }
850 self.segments.0[i].x = first_segs[i].x;
851 }
852 } else {
853 #[cfg(debug_assertions)]
854 {
855 tracing::error!("expected {} segments in `first_segs`, was {}", first_range.len(), first_segs.len());
856 }
857 }
858 }
859 }
860
861 if self.lines.0.len() > 1 {
862 if self.last_line != last {
863 let last_offset = (last.origin - self.last_line.origin).cast::<f32>().cast_unit();
866
867 let last_range = self.lines.segs(self.lines.0.len() - 1);
868 let last_glyphs = self.segments.glyphs_range(last_range);
869
870 for g in &mut self.glyphs[last_glyphs.iter()] {
871 g.point += last_offset;
872 }
873
874 let last_line = self.lines.last_mut();
875 last_line.x_offset = last.origin.x.0 as f32;
876 }
879 if !last_segs.is_empty() {
880 let last_range = self.lines.segs(self.lines.0.len() - 1);
882
883 if last_range.len() == last_segs.len() {
884 for i in last_range.iter() {
885 let li = i - last_range.start();
886
887 let seg_offset = last_segs[li].x - self.segments.0[i].x;
888 let glyphs = self.segments.glyphs(i);
889 for g in &mut self.glyphs[glyphs.iter()] {
890 g.point.x += seg_offset;
891 }
892 self.segments.0[i].x = last_segs[li].x;
893 }
894 } else {
895 #[cfg(debug_assertions)]
896 {
897 tracing::error!("expected {} segments in `last_segs`, was {}", last_range.len(), last_segs.len());
898 }
899 }
900 }
901 }
902
903 self.first_line = first;
904 self.last_line = last;
905
906 let block_size = self.block_size();
907 let align_size = args.constraints.fill_size_or(block_size);
908
909 if self.lines.0.len() > 2 {
910 let mid_offset = euclid::vec2::<f32, Px>(
913 0.0,
914 match block_size.height.cmp(&align_size.height) {
915 cmp::Ordering::Less => (align_size.height - block_size.height).0 as f32 * align_y + mid.0 as f32,
916 cmp::Ordering::Equal => mid.0 as f32,
917 cmp::Ordering::Greater => (align_size.height - block_size.height).0 as f32 * overflow_align_y + mid.0 as f32,
918 },
919 );
920 let y_transform = mid_offset.y - self.mid_offset;
921 let align_width = align_size.width.0 as f32;
922
923 let skip_last = self.lines.0.len() - 2;
924 let mut line_start = self.lines.0[0].end;
925 for line in &mut self.lines.0[1..=skip_last] {
926 let x_offset = if line.width < align_width {
927 (align_width - line.width) * align_x
928 } else {
929 (align_width - line.width) * overflow_align_x
930 };
931 let x_transform = x_offset - line.x_offset;
932
933 let glyphs = self.segments.glyphs_range(IndexRange(line_start, line.end));
934 for g in &mut self.glyphs[glyphs.iter()] {
935 g.point.x += x_transform;
936 g.point.y += y_transform;
937 }
938 line.x_offset = x_offset;
939
940 line_start = line.end;
941 }
942
943 let y_transform_px = Px(y_transform as i32);
944 self.underline -= y_transform_px;
945 self.baseline -= y_transform_px;
946 self.overline -= y_transform_px;
947 self.strikethrough -= y_transform_px;
948 self.underline_descent -= y_transform_px;
949 self.mid_offset = mid_offset.y;
950 }
951
952 let baseline_offset =
954 if self.align.is_baseline() { -self.baseline } else { Px(0) } + if args.align.is_baseline() { self.baseline } else { Px(0) };
955 if baseline_offset != Px(0) {
956 let baseline_offset = baseline_offset.0 as f32;
957 for g in &mut self.glyphs {
958 g.point.y += baseline_offset;
959 }
960 }
961
962 self.align_size = align_size;
963 self.align = args.align;
964 self.direction = args.direction;
965 self.is_inlined = is_inlined;
966
967 self.debug_assert_ranges();
968 }
969 fn reshape_line_height_and_spacing(&mut self, line_height: Px, line_spacing: Px) {
970 let mut update_height = false;
971
972 if self.line_height != line_height {
973 let offset_y = (line_height - self.line_height).0 as f32;
974 let mut offset = 0.0;
975 let center = offset_y / 2.0;
976
977 self.first_line.origin.y += Px(center as i32);
978
979 for (_, r) in self.lines.iter_segs() {
980 let r = self.segments.glyphs_range(r);
981 for g in &mut self.glyphs[r.iter()] {
982 g.point.y += offset + center;
983 }
984
985 offset += offset_y;
986 }
987
988 self.line_height = line_height;
989 update_height = true;
990 }
991
992 if self.line_spacing != line_spacing {
993 if self.lines.is_multi() {
994 let offset_y = (line_spacing - self.line_spacing).0 as f32;
995 let mut offset = offset_y;
996
997 for (_, r) in self.lines.iter_segs_skip(1) {
998 let r = self.segments.glyphs_range(r);
999
1000 for g in &mut self.glyphs[r.iter()] {
1001 g.point.y += offset;
1002 }
1003
1004 offset += offset_y;
1005 }
1006 offset -= offset_y;
1007
1008 self.last_line.origin.y += Px(offset as i32);
1009
1010 update_height = true;
1011 }
1012 self.line_spacing = line_spacing;
1013 }
1014
1015 if update_height {
1016 self.update_mid_size();
1017
1018 if !self.is_inlined {
1019 self.update_first_last_lines();
1020 }
1021 }
1022 }
1023
1024 pub fn clear_reshape(&mut self) {
1026 self.reshape_lines(&TextReshapingArgs::default());
1027 }
1028
1029 fn justify_lines_range(&self) -> ops::Range<usize> {
1030 let mut range = 0..self.lines_len();
1031
1032 if !self.is_inlined {
1033 range.end = range.end.saturating_sub(1);
1035 }
1036 range
1039 }
1040
1041 pub fn reshape_lines_justify(&mut self, mode: Justify, lang: &Lang) {
1046 self.clear_justify_impl(true);
1047
1048 if !self.align.is_fill_x() {
1049 return;
1050 }
1051
1052 let mode = mode.resolve(lang);
1053
1054 let range = self.justify_lines_range();
1055
1056 let fill_width = self.align_size.width.0 as f32;
1057 let last_li = range.end.saturating_sub(1);
1058
1059 for li in range.clone() {
1060 let mut count;
1061 let mut space;
1062 let mut line_seg_range;
1063 let mut offset = 0.0;
1064 let mut last_is_space = false;
1065
1066 let mut fill_width = fill_width;
1067 if self.is_inlined {
1068 if li == 0 {
1070 fill_width = self.first_line.width().0 as f32;
1071 } else if li == last_li {
1072 fill_width = self.last_line.width().0 as f32;
1073 }
1074 }
1075
1076 {
1077 let line = self.line(li).unwrap();
1079
1080 count = match mode {
1082 Justify::InterWord => line.segs().filter(|s| s.kind().is_space()).count(),
1083 Justify::InterLetter => line
1084 .segs()
1085 .map(|s| {
1086 if s.kind().is_space() {
1087 s.clusters_count().saturating_sub(1).max(1)
1088 } else if s.kind().is_word() {
1089 s.clusters_count().saturating_sub(1)
1090 } else {
1091 0
1092 }
1093 })
1094 .sum(),
1095 Justify::Auto => unreachable!(),
1096 };
1097
1098 space = fill_width - self.lines.0[li].width;
1100
1101 line_seg_range = 0..line.segs_len();
1102
1103 let mut first_is_space = false;
1105
1106 if let Some(s) = line.seg(0)
1107 && s.kind().is_space()
1108 && (!self.is_inlined || li > 0 || self.first_line.origin.x == Px(0))
1109 {
1110 first_is_space = true;
1112 count -= 1;
1113 space += s.advance();
1114 }
1115 if let Some(s) = line.seg(line.segs_len().saturating_sub(1))
1116 && s.kind().is_space()
1117 && (!self.is_inlined || li < range.end - 1 || about_eq(self.first_line.size.width.0 as f32, fill_width, 1.0))
1118 {
1119 last_is_space = true;
1121 count -= 1;
1122 space += s.advance();
1123 }
1124 if first_is_space {
1125 line_seg_range.start += 1;
1126 let gsi = self.line(li).unwrap().seg_range.start();
1127 let adv = mem::take(&mut self.segments.0[gsi].advance);
1128 offset -= adv;
1129 self.justified.push(adv);
1130 }
1131 if last_is_space {
1132 line_seg_range.end = line_seg_range.end.saturating_sub(1);
1133 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1134 let adv = mem::take(&mut self.segments.0[gsi].advance);
1135 self.justified.push(adv);
1136 }
1137 if line_seg_range.start > line_seg_range.end {
1138 line_seg_range = 0..0;
1139 }
1140 }
1141 let justify_advance = space / count as f32;
1142 self.justified.push(justify_advance);
1143
1144 for si in line_seg_range {
1145 let is_space;
1146 let glyphs_range;
1147 let gsi;
1148 {
1149 let line = self.line(li).unwrap();
1150 let seg = line.seg(si).unwrap();
1151
1152 is_space = seg.kind().is_space();
1153 glyphs_range = seg.glyphs_range();
1154 gsi = line.seg_range.start() + si;
1155 }
1156
1157 let mut cluster = if self.clusters.is_empty() {
1158 0
1159 } else {
1160 self.clusters[glyphs_range.start()]
1161 };
1162 for gi in glyphs_range {
1163 self.glyphs[gi].point.x += offset;
1164
1165 if matches!(mode, Justify::InterLetter) && self.clusters[gi] != cluster {
1166 cluster = self.clusters[gi];
1167 offset += justify_advance;
1168 self.segments.0[gsi].advance += justify_advance;
1169 }
1170 }
1171
1172 let seg = &mut self.segments.0[gsi];
1173 seg.x += offset;
1174 if is_space {
1175 offset += justify_advance;
1176 seg.advance += justify_advance;
1177 }
1178 }
1179 if last_is_space {
1180 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1181 let seg = &mut self.segments.0[gsi];
1182 debug_assert_eq!(seg.advance, 0.0);
1183 seg.x += offset;
1184 }
1185 self.justified.shrink_to_fit();
1186 }
1187
1188 self.justify = mode;
1189 }
1190
1191 pub fn clear_justify(&mut self) {
1195 self.clear_justify_impl(false)
1196 }
1197 fn clear_justify_impl(&mut self, keep_alloc: bool) {
1198 if self.justify_mode().is_none() {
1199 return;
1200 }
1201
1202 let range = self.justify_lines_range();
1203 debug_assert!(range.len() <= self.justified.len());
1204
1205 let mut justified_alloc = mem::take(&mut self.justified);
1206
1207 let mut justified = justified_alloc.drain(..);
1208 for li in range {
1209 let mut line_seg_range;
1210 let mut last_is_space = false;
1211
1212 let mut offset = 0.0;
1213
1214 {
1215 let line = self.line(li).unwrap();
1216
1217 line_seg_range = 0..line.segs_len();
1218
1219 let mut first_is_space = false;
1221
1222 if let Some(s) = line.seg(0) {
1223 first_is_space = s.kind().is_space();
1224 }
1225 if let Some(s) = line.seg(line.segs_len().saturating_sub(1)) {
1226 last_is_space = s.kind().is_space();
1227 }
1228 if first_is_space {
1229 line_seg_range.start += 1;
1230 let gsi = self.line(li).unwrap().seg_range.start();
1231
1232 let adv = justified.next().unwrap();
1233 self.segments.0[gsi].advance = adv;
1234 offset -= adv;
1235 }
1236 if last_is_space {
1237 line_seg_range.end = line_seg_range.end.saturating_sub(1);
1238 let adv = justified.next().unwrap();
1239 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1240 self.segments.0[gsi].advance = adv;
1241 }
1242 if line_seg_range.start > line_seg_range.end {
1243 line_seg_range = 0..0;
1244 }
1245 }
1246
1247 let justify_advance = justified.next().unwrap();
1248
1249 for si in line_seg_range {
1250 let is_space;
1251 let glyphs_range;
1252 let gsi;
1253 {
1254 let line = self.line(li).unwrap();
1255 let seg = line.seg(si).unwrap();
1256
1257 is_space = seg.kind().is_space();
1258 glyphs_range = seg.glyphs_range();
1259 gsi = line.seg_range.start() + si;
1260 }
1261
1262 let mut cluster = if self.clusters.is_empty() {
1263 0
1264 } else {
1265 self.clusters[glyphs_range.start()]
1266 };
1267 for gi in glyphs_range {
1268 self.glyphs[gi].point.x -= offset;
1269
1270 if matches!(self.justify, Justify::InterLetter) && self.clusters[gi] != cluster {
1271 cluster = self.clusters[gi];
1272 offset += justify_advance;
1273 self.segments.0[gsi].advance -= justify_advance;
1274 }
1275 }
1276
1277 let seg = &mut self.segments.0[gsi];
1278 seg.x -= offset;
1279 if is_space {
1280 offset += justify_advance;
1281 seg.advance -= justify_advance;
1282 }
1283 }
1284 if last_is_space {
1285 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1286 self.segments.0[gsi].x -= offset;
1287 }
1288 }
1289
1290 self.justify = Justify::Auto;
1291
1292 if keep_alloc {
1293 drop(justified);
1294 self.justified = justified_alloc;
1295 }
1296 }
1297
1298 pub fn paragraph_break(&self) -> ParagraphBreak {
1300 self.paragraph_break
1301 }
1302
1303 pub fn line_height(&self) -> Px {
1305 self.line_height
1306 }
1307
1308 pub fn line_spacing(&self) -> Px {
1310 self.line_spacing
1311 }
1312
1313 pub fn paragraph_spacing(&self) -> Px {
1315 self.paragraph_spacing
1316 }
1317
1318 pub fn baseline(&self) -> Px {
1324 self.baseline
1325 }
1326
1327 pub fn overline(&self) -> Px {
1329 self.overline
1330 }
1331
1332 pub fn strikethrough(&self) -> Px {
1334 self.strikethrough
1335 }
1336
1337 pub fn underline(&self) -> Px {
1339 self.underline
1340 }
1341
1342 pub fn underline_descent(&self) -> Px {
1345 self.underline_descent
1346 }
1347
1348 pub fn is_empty(&self) -> bool {
1350 self.segments.0.is_empty()
1351 }
1352
1353 pub fn lines(&self) -> impl Iterator<Item = ShapedLine<'_>> {
1357 let just_width = self.justify_mode().map(|_| self.align_size.width);
1358 self.lines.iter_segs().enumerate().map(move |(i, (w, r))| ShapedLine {
1359 text: self,
1360 seg_range: r,
1361 index: i,
1362 width: just_width.unwrap_or_else(|| Px(w.round() as i32)),
1363 })
1364 }
1365
1366 pub fn lines_len(&self) -> usize {
1368 self.lines.0.len()
1369 }
1370
1371 pub fn first_wrapped(&self) -> bool {
1373 self.first_wrapped
1374 }
1375
1376 pub fn line(&self, line_idx: usize) -> Option<ShapedLine<'_>> {
1378 if line_idx >= self.lines.0.len() {
1379 None
1380 } else {
1381 self.lines.iter_segs_skip(line_idx).next().map(move |(w, r)| ShapedLine {
1382 text: self,
1383 seg_range: r,
1384 index: line_idx,
1385 width: Px(w.round() as i32),
1386 })
1387 }
1388 }
1389
1390 pub fn empty(&self) -> ShapedText {
1392 ShapedText {
1393 glyphs: vec![],
1394 clusters: vec![],
1395 segments: GlyphSegmentVec(vec![]),
1396 lines: LineRangeVec(vec![LineRange {
1397 end: 0,
1398 width: 0.0,
1399 x_offset: 0.0,
1400 directions: LayoutDirections::empty(),
1401 }]),
1402 fonts: FontRangeVec(vec![FontRange {
1403 font: self.fonts.font(0).clone(),
1404 end: 0,
1405 }]),
1406 images: vec![],
1407 orig_line_height: self.orig_line_height,
1408 orig_line_spacing: self.orig_line_spacing,
1409 orig_first_line: PxSize::zero(),
1410 orig_last_line: PxSize::zero(),
1411 line_height: self.orig_line_height,
1412 line_spacing: self.orig_line_spacing,
1413 paragraph_spacing: self.paragraph_spacing,
1414 paragraph_break: self.paragraph_break,
1415 baseline: self.baseline,
1416 overline: self.overline,
1417 strikethrough: self.strikethrough,
1418 underline: self.underline,
1419 underline_descent: self.underline_descent,
1420 mid_offset: 0.0,
1421 align_size: PxSize::zero(),
1422 align: Align::TOP_LEFT,
1423 justify: Justify::Auto,
1424 justified: vec![],
1425 overflow_align: Align::TOP_LEFT,
1426 direction: LayoutDirection::LTR,
1427 first_wrapped: false,
1428 first_line: PxRect::zero(),
1429 mid_clear: Px(0),
1430 is_inlined: false,
1431 mid_size: PxSize::zero(),
1432 last_line: PxRect::zero(),
1433 has_colored_glyphs: false,
1434 }
1435 }
1436
1437 pub fn can_rewrap(&self, max_width: Px) -> bool {
1441 for line in self.lines() {
1442 if line.width > max_width || line.started_by_wrap() {
1443 return true;
1444 }
1445 }
1446 false
1447 }
1448
1449 fn debug_assert_ranges(&self) {
1450 #[cfg(debug_assertions)]
1451 {
1452 #[allow(unused)]
1453 macro_rules! trace_assert {
1454 ($cond:expr $(,)?) => {
1455 #[allow(clippy::all)]
1456 if !($cond) {
1457 tracing::error!("{}", stringify!($cond));
1458 return;
1459 }
1460 };
1461 ($cond:expr, $($arg:tt)+) => {
1462 #[allow(clippy::all)]
1463 if !($cond) {
1464 tracing::error!($($arg)*);
1465 return;
1466 }
1467 };
1468 }
1469
1470 let mut prev_seg_end = 0;
1471 for seg in &self.segments.0 {
1472 trace_assert!(seg.end >= prev_seg_end);
1473 prev_seg_end = seg.end;
1474 }
1475 trace_assert!(self.segments.0.last().map(|s| s.end == self.glyphs.len()).unwrap_or(true));
1476
1477 let mut prev_line_end = 0;
1478 for (i, line) in self.lines.0.iter().enumerate() {
1479 trace_assert!(line.end >= prev_line_end);
1480 trace_assert!(line.width >= 0.0);
1481
1482 let line_max = line.x_offset + line.width;
1483 let glyphs = self.segments.glyphs_range(IndexRange(prev_line_end, line.end));
1484 for g in &self.glyphs[glyphs.iter()] {
1485 trace_assert!(
1489 g.point.x <= line_max,
1490 "glyph.x({:?}) > line[{i}].x+width({:?})",
1491 g.point.x,
1492 line_max
1493 );
1494 }
1495
1496 let seg_width = self.segments.0[prev_line_end..line.end].iter().map(|s| s.advance).sum::<f32>();
1497 trace_assert!(
1498 seg_width <= line.width,
1499 "seg_width({:?}) > line[{i}].width({:?})",
1500 seg_width,
1501 line.width,
1502 );
1503
1504 prev_line_end = line.end;
1505 }
1506 trace_assert!(self.lines.0.last().map(|l| l.end == self.segments.0.len()).unwrap_or(true));
1507
1508 let mut prev_font_end = 0;
1509 for font in &self.fonts.0 {
1510 trace_assert!(font.end >= prev_font_end);
1511 prev_font_end = font.end;
1512 }
1513 trace_assert!(self.fonts.0.last().map(|f| f.end == self.glyphs.len()).unwrap_or(true));
1514 }
1515 }
1516
1517 pub fn caret_origin(&self, caret: CaretIndex, full_text: &str) -> PxPoint {
1519 let index = caret.index;
1520 let mut end_line = None;
1521 for line in self.line(caret.line).into_iter().chain(self.lines()) {
1522 for seg in line.segs() {
1523 let txt_range = seg.text_range();
1524 if !txt_range.contains(&index) {
1525 continue;
1526 }
1527 let local_index = index - txt_range.start;
1528 let is_rtl = seg.direction().is_rtl();
1529
1530 let seg_rect = seg.rect();
1531 let mut origin = seg_rect.origin;
1532
1533 let clusters = seg.clusters();
1534 let mut cluster_i = 0;
1535 let mut search_lig = true;
1536
1537 if is_rtl {
1538 for (i, c) in clusters.iter().enumerate().rev() {
1539 match (*c as usize).cmp(&local_index) {
1540 cmp::Ordering::Less => {
1541 cluster_i = i;
1542 }
1543 cmp::Ordering::Equal => {
1544 cluster_i = i;
1545 search_lig = false;
1546 break;
1547 }
1548 cmp::Ordering::Greater => break,
1549 }
1550 }
1551 } else {
1552 for (i, c) in clusters.iter().enumerate() {
1553 match (*c as usize).cmp(&local_index) {
1554 cmp::Ordering::Less => {
1555 cluster_i = i;
1556 }
1557 cmp::Ordering::Equal => {
1558 cluster_i = i;
1559 search_lig = false;
1560 break;
1561 }
1562 cmp::Ordering::Greater => break,
1563 }
1564 }
1565 }
1566
1567 let mut origin_x = origin.x.0 as f32;
1568
1569 let mut glyph_take = cluster_i;
1575 if is_rtl {
1576 glyph_take += 1;
1577 }
1578
1579 let mut search_lig_data = None;
1580
1581 'outer: for (font, glyphs) in seg.glyphs_with_x_advance() {
1582 for (g, advance) in glyphs {
1583 search_lig_data = Some((font, g.index, advance));
1584
1585 if glyph_take == 0 {
1586 break 'outer;
1587 }
1588 origin_x += advance;
1589 glyph_take -= 1;
1590 }
1591 }
1592
1593 if search_lig && let Some((font, g_index, advance)) = search_lig_data {
1594 let lig_start = txt_range.start + clusters[cluster_i] as usize;
1595 let lig_end = if is_rtl {
1596 if cluster_i == 0 {
1597 txt_range.end
1598 } else {
1599 txt_range.start + clusters[cluster_i - 1] as usize
1600 }
1601 } else {
1602 clusters
1603 .get(cluster_i + 1)
1604 .map(|c| txt_range.start + *c as usize)
1605 .unwrap_or_else(|| txt_range.end)
1606 };
1607
1608 let maybe_lig = &full_text[lig_start..lig_end];
1609
1610 let lig_len = unicode_segmentation::UnicodeSegmentation::grapheme_indices(maybe_lig, true).count();
1611 if lig_len > 1 {
1612 let lig_taken = &full_text[lig_start..index];
1615 let lig_taken = unicode_segmentation::UnicodeSegmentation::grapheme_indices(lig_taken, true).count();
1616
1617 for (i, lig_advance) in font.ligature_caret_offsets(g_index).enumerate() {
1618 if i == lig_taken {
1619 origin_x += lig_advance;
1621 search_lig = false;
1622 break;
1623 }
1624 }
1625
1626 if search_lig {
1627 let lig_advance = advance * (lig_taken as f32 / lig_len as f32);
1629
1630 if is_rtl {
1631 origin_x -= lig_advance;
1632 } else {
1633 origin_x += lig_advance;
1634 }
1635 }
1636 }
1637 }
1638
1639 origin.x = Px(origin_x.round() as _);
1640 return origin;
1641 }
1642
1643 if line.index == caret.line && line.text_range().end == index && line.ended_by_wrap() {
1644 end_line = Some(line.index);
1646 break;
1647 }
1648 }
1649
1650 let line_end = end_line.unwrap_or_else(|| self.lines_len().saturating_sub(1));
1652 if let Some(line) = self.line(line_end) {
1653 let rect = line.rect();
1654 if self.direction().is_rtl() {
1655 PxPoint::new(rect.min_x(), rect.min_y())
1657 } else {
1658 PxPoint::new(rect.max_x(), rect.min_y())
1660 }
1661 } else {
1662 PxPoint::zero()
1663 }
1664 }
1665
1666 pub fn nearest_line(&self, y: Px) -> Option<ShapedLine<'_>> {
1668 let first_line_max_y = self.first_line.max_y();
1669 if first_line_max_y >= y {
1670 self.line(0)
1671 } else if self.last_line.min_y() <= y {
1672 self.line(self.lines_len().saturating_sub(1))
1673 } else {
1674 let y = y - first_line_max_y;
1675 let line = (y / self.line_height()).0 as usize + 1;
1676 self.lines.iter_segs_skip(line).next().map(move |(w, r)| ShapedLine {
1677 text: self,
1678 seg_range: r,
1679 index: line,
1680 width: Px(w.round() as i32),
1681 })
1682 }
1683 }
1684
1685 pub fn snap_caret_line(&self, mut caret: CaretIndex) -> CaretIndex {
1689 for line in self.lines() {
1690 let range = line.text_range();
1691
1692 if range.start == caret.index {
1693 if line.started_by_wrap() {
1695 if caret.line >= line.index {
1696 caret.line = line.index;
1697 } else {
1698 caret.line = line.index.saturating_sub(1);
1699 }
1700 } else {
1701 caret.line = line.index;
1702 }
1703 return caret;
1704 } else if range.contains(&caret.index) {
1705 caret.line = line.index;
1707 return caret;
1708 }
1709 }
1710 caret.line = self.lines.0.len().saturating_sub(1);
1711 caret
1712 }
1713
1714 pub fn overflow_info(&self, max_size: PxSize, overflow_suffix_width: Px) -> Option<TextOverflowInfo> {
1716 let (last_line, overflow_line) = match self.overflow_line(max_size.height) {
1719 Some(l) => {
1720 if l.index == 0 {
1721 return Some(TextOverflowInfo {
1723 line: 0,
1724 text_char: 0,
1725 included_glyphs: smallvec::smallvec![],
1726 suffix_origin: l.rect().origin.cast().cast_unit(),
1727 });
1728 } else {
1729 (self.line(l.index - 1).unwrap(), l.index)
1730 }
1731 }
1732 None => (self.line(self.lines_len().saturating_sub(1))?, self.lines_len()),
1733 };
1734
1735 let max_width = max_size.width - overflow_suffix_width;
1738
1739 if last_line.width <= max_width {
1740 return if overflow_line < self.lines_len() {
1742 Some(TextOverflowInfo {
1743 line: overflow_line,
1744 text_char: last_line.text_range().end,
1745 included_glyphs: smallvec::smallvec_inline![0..last_line.glyphs_range().end()],
1746 suffix_origin: {
1747 let r = last_line.rect();
1748 let mut o = r.origin;
1749 match self.direction {
1750 LayoutDirection::LTR => o.x += r.width(),
1751 LayoutDirection::RTL => o.x -= overflow_suffix_width,
1752 }
1753 o.cast().cast_unit()
1754 },
1755 })
1756 } else {
1757 None
1758 };
1759 }
1760
1761 let directions = last_line.directions();
1762 if directions == LayoutDirections::BIDI {
1763 let mut included_glyphs = smallvec::SmallVec::<[ops::Range<usize>; 1]>::new_const();
1764
1765 let min_x = match self.direction {
1766 LayoutDirection::LTR => Px(0),
1767 LayoutDirection::RTL => last_line.rect().max_x() - max_width,
1768 };
1769 let max_x = min_x + max_width;
1770
1771 let mut end_seg = None;
1772
1773 for seg in last_line.segs() {
1774 let (x, width) = seg.x_width();
1775 let seg_max_x = x + width;
1776
1777 if x < max_x && seg_max_x >= min_x {
1778 let mut glyphs_range = seg.glyphs_range().iter();
1779 let mut text_range = seg.text_range();
1780 if x < min_x {
1781 if let Some((c, g)) = seg.overflow_char_glyph((width - (min_x - x)).0 as f32) {
1782 glyphs_range.start += g + 1;
1783 text_range.start += c;
1784 }
1785 } else if seg_max_x > max_x
1786 && let Some((c, g)) = seg.overflow_char_glyph((width - seg_max_x - max_x).0 as f32)
1787 {
1788 glyphs_range.end -= g;
1789 text_range.end -= c;
1790 }
1791
1792 if let Some(l) = included_glyphs.last_mut() {
1793 if l.end == glyphs_range.start {
1794 l.end = glyphs_range.end;
1795 } else if glyphs_range.end == l.start {
1796 l.start = glyphs_range.start;
1797 } else {
1798 included_glyphs.push(glyphs_range.clone());
1799 }
1800 } else {
1801 included_glyphs.push(glyphs_range.clone());
1802 }
1803
1804 match self.direction {
1805 LayoutDirection::LTR => {
1806 if let Some((sx, se, gr, tr)) = &mut end_seg {
1807 if x < *sx {
1808 *sx = x;
1809 *se = seg;
1810 *gr = glyphs_range;
1811 *tr = text_range;
1812 }
1813 } else {
1814 end_seg = Some((x, seg, glyphs_range, text_range));
1815 }
1816 }
1817 LayoutDirection::RTL => {
1818 if let Some((smx, se, gr, tr)) = &mut end_seg {
1819 if seg_max_x < *smx {
1820 *smx = seg_max_x;
1821 *se = seg;
1822 *gr = glyphs_range;
1823 *tr = text_range;
1824 }
1825 } else {
1826 end_seg = Some((seg_max_x, seg, glyphs_range, text_range));
1827 }
1828 }
1829 }
1830 }
1831 }
1832
1833 if let Some((_, seg, glyphs_range, text_range)) = end_seg {
1834 Some(match self.direction {
1835 LayoutDirection::LTR => TextOverflowInfo {
1836 line: overflow_line,
1837 text_char: text_range.end,
1838 included_glyphs,
1839 suffix_origin: {
1840 let r = seg.rect();
1841 let seg_range = seg.glyphs_range().iter();
1842 let mut o = r.origin.cast().cast_unit();
1843 let mut w = r.width();
1844 if seg_range != glyphs_range
1845 && let Some(g) = seg.glyph(glyphs_range.end - seg_range.start)
1846 {
1847 o.x = g.1.point.x;
1848 w = Px(0);
1849 }
1850 o.x += w.0 as f32;
1851 o
1852 },
1853 },
1854 LayoutDirection::RTL => TextOverflowInfo {
1855 line: overflow_line,
1856 text_char: text_range.start,
1857 included_glyphs,
1858 suffix_origin: {
1859 let r = seg.rect();
1860 let mut o = r.origin.cast().cast_unit();
1861 let seg_range = seg.glyphs_range().iter();
1862 if seg_range != glyphs_range
1863 && let Some(g) = seg.glyph(glyphs_range.start - seg_range.start)
1864 {
1865 o.x = g.1.point.x;
1866 }
1867 o.x -= overflow_suffix_width.0 as f32;
1868 o
1869 },
1870 },
1871 })
1872 } else {
1873 None
1874 }
1875 } else {
1876 let mut max_width_f32 = max_width.0 as f32;
1878 for seg in last_line.segs() {
1879 let seg_advance = seg.advance();
1880 max_width_f32 -= seg_advance;
1881 if max_width_f32 <= 0.0 {
1882 let seg_text_range = seg.text_range();
1883 let seg_glyphs_range = seg.glyphs_range();
1884
1885 if directions == LayoutDirections::RTL {
1886 let (c, g) = match seg.overflow_char_glyph(seg_advance + max_width_f32) {
1887 Some(r) => r,
1888 None => (seg_text_range.len(), seg_glyphs_range.len()),
1889 };
1890
1891 return Some(TextOverflowInfo {
1892 line: overflow_line,
1893 text_char: seg_text_range.start + c,
1894 included_glyphs: smallvec::smallvec![
1895 0..seg_glyphs_range.start(),
1896 seg_glyphs_range.start() + g + 1..seg_glyphs_range.end()
1897 ],
1898 suffix_origin: {
1899 let mut o = if let Some(g) = seg.glyph(g + 1) {
1900 euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1901 } else {
1902 let rect = seg.rect();
1903 let mut o = rect.origin.cast().cast_unit();
1904 o.x += seg.advance();
1905 o
1906 };
1907 o.x -= overflow_suffix_width.0 as f32;
1908 o
1909 },
1910 });
1911 } else {
1912 let (c, g) = match seg.overflow_char_glyph((max_width - seg.x_width().0).0 as f32) {
1915 Some(r) => r,
1916 None => (seg_text_range.len(), seg_glyphs_range.len()),
1917 };
1918
1919 return Some(TextOverflowInfo {
1920 line: overflow_line,
1921 text_char: seg_text_range.start + c,
1922 included_glyphs: smallvec::smallvec_inline![0..seg_glyphs_range.start() + g],
1923 suffix_origin: {
1924 if let Some(g) = seg.glyph(g) {
1925 euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1926 } else {
1927 let rect = seg.rect();
1928 let mut o = rect.origin.cast().cast_unit();
1929 o.x += seg.advance();
1930 o
1931 }
1932 },
1933 });
1934 }
1935 }
1936 }
1937 None
1939 }
1940 }
1941
1942 pub fn highlight_rects(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = PxRect> + '_ {
1946 let start_origin = self.caret_origin(range.start, full_txt).x;
1947 let end_origin = self.caret_origin(range.end, full_txt).x;
1948
1949 MergingRectIter::new(
1950 self.lines()
1951 .skip(range.start.line)
1952 .take(range.end.line + 1 - range.start.line)
1953 .flat_map(|l| l.segs())
1954 .skip_while(move |s| s.text_end() <= range.start.index)
1955 .take_while(move |s| s.text_start() < range.end.index)
1956 .map(move |s| {
1957 let mut r = s.rect();
1958
1959 if s.text_start() <= range.start.index {
1960 match s.direction() {
1963 LayoutDirection::LTR => {
1964 r.size.width = r.max_x() - start_origin;
1965 r.origin.x = start_origin;
1966 }
1967 LayoutDirection::RTL => {
1968 r.size.width = start_origin - r.origin.x;
1969 }
1970 }
1971 }
1972 if s.text_end() > range.end.index {
1973 match s.direction() {
1976 LayoutDirection::LTR => {
1977 r.size.width = end_origin - r.origin.x;
1978 }
1979 LayoutDirection::RTL => {
1980 r.size.width = r.max_x() - end_origin;
1981 r.origin.x = end_origin;
1982 }
1983 }
1984 }
1985
1986 r
1987 }),
1988 )
1989 }
1990
1991 pub fn highlight_underlines(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = (PxPoint, Px)> + '_ {
1997 let offset = self.underline();
1998 self.highlight_rects(range, full_txt).map(move |r| {
1999 let mut origin = r.origin;
2000 origin.y = r.max_y() - offset;
2001 (origin, r.width())
2002 })
2003 }
2004
2005 pub fn clip_lines(
2007 &self,
2008 clip_range: ops::Range<CaretIndex>,
2009 clip_out: bool,
2010 txt: &str,
2011 lines: impl Iterator<Item = (PxPoint, Px)>,
2012 ) -> Vec<(PxPoint, Px)> {
2013 let clips: Vec<_> = self.highlight_rects(clip_range, txt).collect();
2014
2015 let mut out_lines = vec![];
2016
2017 if clip_out {
2018 let mut exclude_buf = vec![];
2019 for (origin, width) in lines {
2020 let line_max = origin.x + width;
2021
2022 for clip in clips.iter() {
2023 if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2024 if origin.x < clip.max_x() && line_max > clip.origin.x {
2026 exclude_buf.push((clip.origin.x, clip.max_x()));
2028 }
2029 }
2030 }
2031
2032 if !exclude_buf.is_empty() {
2033 exclude_buf.sort_by_key(|(s, _)| *s);
2035
2036 if origin.x < exclude_buf[0].0 {
2037 out_lines.push((origin, exclude_buf[0].0 - origin.x));
2039 }
2040 let mut blank_start = exclude_buf[0].1;
2041 for (clip_start, clip_end) in exclude_buf.drain(..).skip(1) {
2042 if clip_start > blank_start {
2043 if line_max > clip_start {
2045 out_lines.push((PxPoint::new(blank_start, origin.y), line_max.min(clip_start) - blank_start));
2047 }
2048 blank_start = clip_end;
2049 }
2050 }
2051 if line_max > blank_start {
2052 out_lines.push((PxPoint::new(blank_start, origin.y), line_max - blank_start));
2054 }
2055 } else {
2056 out_lines.push((origin, width));
2058 }
2059 }
2060 } else {
2061 let mut include_buf = vec![];
2062 for (origin, width) in lines {
2063 let line_max = origin.x + width;
2064
2065 for clip in clips.iter() {
2066 if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2067 if origin.x < clip.max_x() && line_max > clip.origin.x {
2069 include_buf.push((clip.origin.x, clip.max_x()));
2071 }
2072 }
2073 }
2074
2075 if !include_buf.is_empty() {
2076 include_buf.sort_by_key(|(s, _)| *s);
2077
2078 for (clip_start, clip_end) in include_buf.drain(..) {
2079 let start = clip_start.max(origin.x);
2080 let end = clip_end.min(line_max);
2081
2082 out_lines.push((PxPoint::new(start, origin.y), end - start));
2083 }
2084
2085 include_buf.clear();
2086 }
2087 }
2088 }
2089
2090 out_lines
2091 }
2092}
2093
2094struct ImageGlyphsIter<'a, G>
2095where
2096 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2097{
2098 glyphs: G,
2099 glyphs_i: u32,
2100 images: &'a [(u32, GlyphImage)],
2101 maybe_img: Option<(&'a Font, &'a [GlyphInstance])>,
2102}
2103impl<'a, G> Iterator for ImageGlyphsIter<'a, G>
2104where
2105 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2106{
2107 type Item = (&'a Font, ShapedImageGlyphs<'a>);
2108
2109 fn next(&mut self) -> Option<Self::Item> {
2110 loop {
2111 if let Some((font, glyphs)) = &mut self.maybe_img {
2112 while self.images.first().map(|(i, _)| *i < self.glyphs_i).unwrap_or(false) {
2116 self.images = &self.images[1..];
2117 }
2118
2119 if let Some((i, img)) = self.images.first() {
2120 if *i == self.glyphs_i {
2122 self.glyphs_i += 1;
2124 let mut size = img.0.with(|i| i.size()).cast::<f32>();
2125 let scale = font.size().0 as f32 / size.width.max(size.height);
2126 size *= scale;
2127 let r = (
2128 *font,
2129 ShapedImageGlyphs::Image {
2130 rect: euclid::Rect::new(glyphs[0].point - euclid::vec2(0.0, size.height), size),
2131 base_glyph: glyphs[0].index,
2132 img: &img.0,
2133 },
2134 );
2135 *glyphs = &glyphs[1..];
2136 if glyphs.is_empty() {
2137 self.maybe_img = None;
2138 }
2139 return Some(r);
2140 } else {
2141 let normal = &glyphs[..glyphs.len().min(*i as _)];
2143 self.glyphs_i += normal.len() as u32;
2144
2145 *glyphs = &glyphs[normal.len()..];
2146 let r = (*font, ShapedImageGlyphs::Normal(normal));
2147
2148 if glyphs.is_empty() {
2149 self.maybe_img = None;
2150 }
2151 return Some(r);
2152 }
2153 } else {
2154 let r = (*font, ShapedImageGlyphs::Normal(glyphs));
2156 self.maybe_img = None;
2157 return Some(r);
2158 }
2159 } else {
2160 let seq = self.glyphs.next()?;
2161 self.maybe_img = Some(seq);
2163 }
2164 }
2165 }
2166}
2167
2168struct ColoredGlyphsIter<'a, G>
2169where
2170 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2171{
2172 glyphs: G,
2173 maybe_colored: Option<(&'a Font, &'a [GlyphInstance])>,
2174}
2175impl<'a, G> Iterator for ColoredGlyphsIter<'a, G>
2176where
2177 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2178{
2179 type Item = (&'a Font, ShapedColoredGlyphs<'a>);
2180
2181 fn next(&mut self) -> Option<Self::Item> {
2182 loop {
2183 if let Some((font, glyphs)) = self.maybe_colored {
2184 let color_glyphs = font.face().color_glyphs();
2187
2188 for (i, g) in glyphs.iter().enumerate() {
2189 if let Some(c_glyphs) = color_glyphs.glyph(g.index) {
2190 let next_start = i + 1;
2193 if next_start < glyphs.len() {
2194 self.maybe_colored = Some((font, &glyphs[next_start..]));
2196 } else {
2197 self.maybe_colored = None;
2199 }
2200
2201 return Some((
2202 font,
2203 ShapedColoredGlyphs::Colored {
2204 point: g.point,
2205 base_glyph: g.index,
2206 glyphs: c_glyphs,
2207 },
2208 ));
2209 }
2210 }
2211 self.maybe_colored = None;
2213
2214 debug_assert!(!glyphs.is_empty());
2216 return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2217 } else if let Some((font, glyphs)) = self.glyphs.next() {
2218 let color_glyphs = font.face().color_glyphs();
2221 if color_glyphs.is_empty() {
2222 return Some((font, ShapedColoredGlyphs::Normal(glyphs)));
2223 } else {
2224 self.maybe_colored = Some((font, glyphs));
2226 continue;
2227 }
2228 } else {
2229 return None;
2230 }
2231 }
2232 }
2233}
2234
2235#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2239#[non_exhaustive]
2240pub struct TextOverflowInfo {
2241 pub line: usize,
2248
2249 pub text_char: usize,
2254
2255 pub included_glyphs: smallvec::SmallVec<[ops::Range<usize>; 1]>,
2260
2261 pub suffix_origin: euclid::Point2D<f32, Px>,
2266}
2267
2268trait FontListRef {
2269 fn shape_segment<R>(
2271 &self,
2272 seg: &str,
2273 word_ctx_key: &WordContextKey,
2274 features: &[harfrust::Feature],
2275 out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2276 ) -> R;
2277}
2278impl FontListRef for [Font] {
2279 fn shape_segment<R>(
2280 &self,
2281 seg: &str,
2282 word_ctx_key: &WordContextKey,
2283 features: &[harfrust::Feature],
2284 out: impl FnOnce(&ShapedSegmentData, &Font) -> R,
2285 ) -> R {
2286 let mut out = Some(out);
2287 let last = self.len() - 1;
2288 for font in &self[..last] {
2289 let r = font.shape_segment(seg, word_ctx_key, features, |seg| {
2290 if seg.glyphs.iter().all(|g| g.index != 0) {
2291 Some(out.take().unwrap()(seg, font))
2292 } else {
2293 None
2294 }
2295 });
2296 if let Some(r) = r {
2297 return r;
2298 }
2299 }
2300 self[last].shape_segment(seg, word_ctx_key, features, move |seg| out.unwrap()(seg, &self[last]))
2301 }
2302}
2303
2304struct ShapedTextBuilder {
2305 out: ShapedText,
2306
2307 line_height: f32,
2308 line_spacing: f32,
2309 paragraph_spacing: f32,
2310 word_spacing: f32,
2311 letter_spacing: f32,
2312 paragraph_indent: (f32, bool),
2313 max_width: f32,
2314 break_words: bool,
2315 hyphen_glyphs: (ShapedSegmentData, Font),
2316 tab_x_advance: f32,
2317 tab_index: u32,
2318 hyphens: Hyphens,
2319 lang: Lang,
2320
2321 origin: euclid::Point2D<f32, ()>,
2322 allow_first_wrap: bool,
2323 first_line_max: f32,
2324 mid_clear_min: f32,
2325 max_line_x: f32,
2326 text_seg_end: usize,
2327 line_has_ltr: bool,
2328 line_has_rtl: bool,
2329}
2330impl ShapedTextBuilder {
2331 fn actual_max_width(&self) -> f32 {
2332 if self.out.lines.0.is_empty() && !self.out.first_wrapped {
2333 self.first_line_max.min(self.max_width)
2334 } else {
2335 self.max_width
2336 }
2337 }
2338
2339 fn shape_text(fonts: &[Font], text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
2340 let _span = tracing::trace_span!("shape_text").entered();
2341 let mut t = Self {
2342 out: ShapedText {
2343 glyphs: Default::default(),
2344 clusters: Default::default(),
2345 segments: Default::default(),
2346 lines: Default::default(),
2347 fonts: Default::default(),
2348 line_height: Default::default(),
2349 line_spacing: Default::default(),
2350 orig_line_height: Default::default(),
2351 orig_line_spacing: Default::default(),
2352 orig_first_line: Default::default(),
2353 orig_last_line: Default::default(),
2354 paragraph_break: Default::default(),
2355 paragraph_spacing: Default::default(),
2356 baseline: Default::default(),
2357 overline: Default::default(),
2358 strikethrough: Default::default(),
2359 underline: Default::default(),
2360 underline_descent: Default::default(),
2361 mid_offset: 0.0,
2362 align_size: PxSize::zero(),
2363 align: Align::TOP_LEFT,
2364 justify: Justify::Auto,
2365 justified: vec![],
2366 overflow_align: Align::TOP_LEFT,
2367 direction: LayoutDirection::LTR,
2368 first_wrapped: false,
2369 is_inlined: config.inline_constraints.is_some(),
2370 first_line: PxRect::zero(),
2371 mid_clear: Px(0),
2372 mid_size: PxSize::zero(),
2373 last_line: PxRect::zero(),
2374 has_colored_glyphs: false,
2375 images: vec![],
2376 },
2377
2378 line_height: 0.0,
2379 line_spacing: 0.0,
2380 paragraph_spacing: 0.0,
2381 word_spacing: 0.0,
2382 letter_spacing: 0.0,
2383 paragraph_indent: (0.0, false),
2384 max_width: 0.0,
2385 break_words: false,
2386 hyphen_glyphs: (ShapedSegmentData::default(), fonts[0].clone()),
2387 tab_x_advance: 0.0,
2388 tab_index: 0,
2389 hyphens: config.hyphens,
2390 lang: config.lang.clone(),
2391 allow_first_wrap: false,
2392
2393 origin: euclid::point2(0.0, 0.0),
2394 first_line_max: f32::INFINITY,
2395 mid_clear_min: 0.0,
2396 max_line_x: 0.0,
2397 text_seg_end: 0,
2398 line_has_ltr: false,
2399 line_has_rtl: false,
2400 };
2401
2402 let mut word_ctx_key = WordContextKey::new(&config.lang, config.direction, &config.font_features);
2403
2404 let metrics = fonts[0].metrics();
2405
2406 t.out.orig_line_height = config.line_height;
2407 t.out.orig_line_spacing = config.line_spacing;
2408 t.out.line_height = config.line_height;
2409 t.out.line_spacing = config.line_spacing;
2410 t.out.paragraph_spacing = config.paragraph_spacing;
2411 t.out.paragraph_break = config.paragraph_break;
2412
2413 t.line_height = config.line_height.0 as f32;
2414 t.line_spacing = config.line_spacing.0 as f32;
2415 t.paragraph_spacing = config.paragraph_spacing.0 as f32;
2416 t.paragraph_indent = (config.paragraph_indent.0.0 as f32, config.paragraph_indent.1);
2417 let baseline = metrics.ascent + metrics.line_gap / 2.0;
2418
2419 t.out.baseline = t.out.line_height - baseline;
2420 t.out.underline = t.out.baseline + metrics.underline_position;
2421 t.out.underline_descent = t.out.baseline + metrics.descent + Px(1);
2422 t.out.strikethrough = t.out.baseline + metrics.ascent / 3.0;
2423 t.out.overline = t.out.baseline + metrics.ascent;
2424
2425 let dft_line_height = metrics.line_height().0 as f32;
2426 let center_height = (t.line_height - dft_line_height) / 2.0;
2427
2428 t.origin = euclid::point2::<_, ()>(0.0, baseline.0 as f32 + center_height);
2429 if !t.paragraph_indent.1 {
2430 t.origin.x = t.paragraph_indent.0;
2432 }
2433
2434 t.max_line_x = 0.0;
2435 if let Some(inline) = config.inline_constraints {
2436 t.first_line_max = inline.first_max.0 as f32;
2437 t.mid_clear_min = inline.mid_clear_min.0 as f32;
2438 t.allow_first_wrap = true;
2439 } else {
2440 t.first_line_max = f32::INFINITY;
2441 t.mid_clear_min = 0.0;
2442 t.allow_first_wrap = false;
2443 }
2444
2445 t.letter_spacing = config.letter_spacing.0 as f32;
2446 t.word_spacing = config.word_spacing.0 as f32;
2447 t.tab_x_advance = config.tab_x_advance.0 as f32;
2448 t.tab_index = fonts[0].space_index();
2449
2450 t.max_width = if config.max_width == Px::MAX {
2451 f32::INFINITY
2452 } else {
2453 config.max_width.0 as f32
2454 };
2455
2456 t.break_words = match config.word_break {
2457 WordBreak::Normal => {
2458 lang!("ch").matches(&config.lang, true, false)
2459 || lang!("jp").matches(&config.lang, true, false)
2460 || lang!("ko").matches(&config.lang, true, false)
2461 }
2462 WordBreak::BreakAll => true,
2463 WordBreak::KeepAll => false,
2464 };
2465
2466 if !matches!(config.hyphens, Hyphens::None) && t.max_width.is_finite() && config.obscuring_char.is_none() {
2467 t.hyphen_glyphs = fonts.shape_segment(config.hyphen_char.as_str(), &word_ctx_key, &config.font_features, |s, f| {
2469 (s.clone(), f.clone())
2470 });
2471 }
2472
2473 if let Some(c) = config.obscuring_char {
2474 t.push_obscured_text(fonts, &config.font_features, &mut word_ctx_key, text, c);
2475 } else {
2476 t.push_text(fonts, &config.font_features, &mut word_ctx_key, text);
2477 }
2478
2479 t.out.glyphs.shrink_to_fit();
2480 t.out.clusters.shrink_to_fit();
2481 t.out.segments.0.shrink_to_fit();
2482 t.out.lines.0.shrink_to_fit();
2483 t.out.fonts.0.shrink_to_fit();
2484 t.out.images.shrink_to_fit();
2485
2486 t.out.debug_assert_ranges();
2487 t.out
2488 }
2489
2490 fn push_obscured_text(
2491 &mut self,
2492 fonts: &[Font],
2493 features: &RFontFeatures,
2494 word_ctx_key: &mut WordContextKey,
2495 text: &SegmentedText,
2496 obscuring_char: char,
2497 ) {
2498 if text.is_empty() {
2499 self.push_last_line(text);
2500 self.push_font(&fonts[0]);
2501 return;
2502 }
2503
2504 let (glyphs, font) = fonts.shape_segment(Txt::from_char(obscuring_char).as_str(), word_ctx_key, features, |s, f| {
2505 (s.clone(), f.clone())
2506 });
2507
2508 for (seg, info) in text.iter() {
2509 let mut seg_glyphs = ShapedSegmentData::default();
2510 for (cluster, _) in seg.char_indices() {
2511 let i = seg_glyphs.glyphs.len();
2512 seg_glyphs.glyphs.extend(glyphs.glyphs.iter().copied());
2513 for g in &mut seg_glyphs.glyphs[i..] {
2514 g.point.0 += seg_glyphs.x_advance;
2515 g.cluster = cluster as u32;
2516 }
2517 seg_glyphs.x_advance += glyphs.x_advance;
2518 }
2519 self.push_glyphs(&seg_glyphs, self.letter_spacing);
2520 self.push_text_seg(seg, info);
2521 }
2522
2523 self.push_last_line(text);
2524
2525 self.push_font(&font);
2526 }
2527
2528 fn push_text(&mut self, fonts: &[Font], features: &RFontFeatures, word_ctx_key: &mut WordContextKey, text: &SegmentedText) {
2529 static LIG: [&[u8]; 4] = [b"liga", b"clig", b"dlig", b"hlig"];
2530 let ligature_enabled = fonts[0].face().has_ligatures()
2531 && features.iter().any(|f| {
2532 let tag = f.tag.to_be_bytes();
2533 LIG.iter().any(|l| *l == tag)
2534 });
2535
2536 let push_words_fn = if ligature_enabled {
2537 Self::push_word_segs_with_lig
2538 } else {
2539 Self::push_word_segs
2540 };
2541
2542 let mut info_start = 0;
2544 let mut words_start = None;
2545 for (i, info) in text.segs().iter().enumerate() {
2546 if info.kind.is_word() {
2547 if words_start.is_none() {
2548 words_start = Some(i);
2549 }
2550 } else {
2551 if let Some(s) = words_start.take() {
2552 push_words_fn(self, fonts, features, word_ctx_key, text, s, i);
2554 }
2555
2556 let seg = &text.text()[info_start..info.end];
2558 self.push_single_seg(fonts, features, word_ctx_key, text, seg, *info);
2559 }
2560 info_start = info.end;
2561 }
2562 if let Some(s) = words_start.take() {
2563 push_words_fn(self, fonts, features, word_ctx_key, text, s, text.segs().len());
2565 }
2566 self.push_last_line(text);
2567
2568 self.push_font(&fonts[0]);
2569 }
2570 fn push_word_segs_with_lig(
2571 &mut self,
2572 fonts: &[Font],
2573 features: &RFontFeatures,
2574 word_ctx_key: &mut WordContextKey,
2575 text: &SegmentedText,
2576 words_start: usize,
2577 words_end: usize,
2578 ) {
2579 let seg_start = if words_start == 0 { 0 } else { text.segs()[words_start - 1].end };
2580 let end_info = text.segs()[words_end - 1];
2581 let seg_end = end_info.end;
2582 let seg = &text.text()[seg_start..seg_end];
2583
2584 if words_end - words_start == 1 {
2585 return self.push_single_seg(fonts, features, word_ctx_key, text, seg, end_info);
2586 }
2587
2588 let handled = fonts[0].shape_segment(seg, word_ctx_key, features, |shaped_seg| {
2590 let mut cluster_start = 0;
2591 let mut cluster_end = None;
2592 for g in shaped_seg.glyphs.iter() {
2593 if g.index == 0 {
2594 return false;
2596 }
2597 if seg[cluster_start as usize..g.cluster as usize].chars().take(2).count() > 1 {
2598 cluster_end = Some(g.index);
2599 break;
2600 }
2601 cluster_start = g.cluster;
2602 }
2603
2604 if cluster_end.is_none() && seg[cluster_start as usize..].chars().take(2).count() > 1 {
2605 cluster_end = Some(seg.len() as u32);
2606 }
2607 if let Some(cluster_end) = cluster_end {
2608 let cluster_start_in_txt = seg_start + cluster_start as usize;
2610 let cluster_end_in_txt = seg_start + cluster_end as usize;
2611
2612 let handle = text.segs()[words_start..words_end]
2613 .iter()
2614 .any(|info| info.end > cluster_start_in_txt && info.end <= cluster_end_in_txt);
2615
2616 if handle {
2617 let max_width = self.actual_max_width();
2618 if self.origin.x + shaped_seg.x_advance > max_width {
2619 if shaped_seg.x_advance > max_width {
2621 return false;
2623 }
2624
2625 self.push_line_break(true, text);
2626 self.push_glyphs(shaped_seg, self.letter_spacing);
2627 }
2628 self.push_glyphs(shaped_seg, self.letter_spacing);
2629 let mut seg = seg;
2630 for info in &text.segs()[words_start..words_end] {
2631 self.push_text_seg(seg, *info);
2632 seg = "";
2633 }
2634
2635 return true;
2636 }
2637 }
2638
2639 false
2640 });
2641
2642 if !handled {
2643 self.push_word_segs(fonts, features, word_ctx_key, text, words_start, words_end);
2644 }
2645 }
2646 fn push_word_segs(
2647 &mut self,
2648 fonts: &[Font],
2649 features: &RFontFeatures,
2650 word_ctx_key: &mut WordContextKey,
2651 text: &SegmentedText,
2652 words_start: usize,
2653 words_end: usize,
2654 ) {
2655 let seg_start = if words_start == 0 { 0 } else { text.segs()[words_start - 1].end };
2656 let end_info = text.segs()[words_end - 1];
2657 let seg_end = end_info.end;
2658 let seg = &text.text()[seg_start..seg_end];
2659
2660 if words_end - words_start == 1 {
2661 return self.push_single_seg(fonts, features, word_ctx_key, text, seg, end_info);
2662 }
2663
2664 let word_segs = move || {
2665 let mut seg_start = seg_start;
2666 (words_start..words_end).map(move |i| {
2667 let info = text.segs()[i];
2668 let seg = &text.text()[seg_start..info.end];
2669 seg_start = info.end;
2670
2671 (seg, info)
2672 })
2673 };
2674
2675 let max_width = self.actual_max_width();
2676 let mut line_break = false;
2677 let mut x_advance = 0.0;
2678 for (seg, info) in word_segs() {
2679 debug_assert!(info.kind.is_word());
2680 word_ctx_key.direction = info.direction();
2681 fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, _| {
2682 x_advance += shaped_seg.x_advance;
2683 });
2684 if self.origin.x + x_advance > max_width {
2685 if x_advance > max_width {
2687 for (seg, info) in word_segs() {
2689 self.push_single_seg(fonts, features, word_ctx_key, text, seg, info);
2690 }
2691 return;
2692 }
2693 line_break = true;
2695 }
2696 }
2697
2698 if line_break {
2699 self.push_line_break(true, text);
2700 }
2701 for (seg, info) in word_segs() {
2702 word_ctx_key.direction = info.direction();
2703 fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2704 self.push_glyphs(shaped_seg, self.letter_spacing);
2705 self.push_text_seg(seg, info);
2706 if matches!(info.kind, TextSegmentKind::Emoji) {
2707 self.push_emoji_seg(shaped_seg, font);
2708 }
2709 self.push_font(font);
2710 });
2711 }
2712 }
2713 fn push_single_seg(
2714 &mut self,
2715 fonts: &[Font],
2716 features: &RFontFeatures,
2717 word_ctx_key: &mut WordContextKey,
2718 text: &SegmentedText,
2719 seg: &str,
2720 info: TextSegment,
2721 ) {
2722 word_ctx_key.direction = info.direction();
2723 if info.kind.is_word() {
2724 let max_width = self.actual_max_width();
2725
2726 fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2727 if self.origin.x + shaped_seg.x_advance > max_width {
2728 if shaped_seg.x_advance > max_width {
2730 let hyphenated = self.push_hyphenate(seg, font, shaped_seg, info, text);
2734
2735 if !hyphenated && self.break_words {
2736 self.push_split_seg(shaped_seg, seg, info, self.letter_spacing, text);
2738 } else if !hyphenated {
2739 let current_start = if self.out.lines.0.is_empty() {
2740 0
2741 } else {
2742 self.out.lines.last().end
2743 };
2744 if !self.out.segments.0[current_start..].is_empty() {
2745 self.push_line_break(true, text);
2746 } else if current_start == 0 && self.allow_first_wrap {
2747 self.out.first_wrapped = true;
2748 }
2749 self.push_glyphs(shaped_seg, self.letter_spacing);
2750 self.push_text_seg(seg, info);
2751 }
2752 } else {
2753 self.push_line_break(true, text);
2754 self.push_glyphs(shaped_seg, self.letter_spacing);
2755 self.push_text_seg(seg, info);
2756 }
2757 } else {
2758 self.push_glyphs(shaped_seg, self.letter_spacing);
2760 self.push_text_seg(seg, info);
2761 }
2762
2763 if matches!(info.kind, TextSegmentKind::Emoji) {
2764 self.push_emoji_seg(shaped_seg, font);
2765 }
2766
2767 self.push_font(font);
2768 });
2769 } else if info.kind.is_space() {
2770 if matches!(info.kind, TextSegmentKind::Tab) {
2771 let max_width = self.actual_max_width();
2772 for (i, _) in seg.char_indices() {
2773 if self.origin.x + self.tab_x_advance > max_width {
2774 self.push_line_break(true, text);
2776 }
2777 let point = euclid::point2(self.origin.x, self.origin.y);
2778 self.origin.x += self.tab_x_advance;
2779 self.out.glyphs.push(GlyphInstance::new(self.tab_index, point));
2780 self.out.clusters.push(i as u32);
2781 }
2782
2783 self.push_text_seg(seg, info);
2784 self.push_font(&fonts[0]);
2785 } else {
2786 let max_width = self.actual_max_width();
2787 fonts.shape_segment(seg, word_ctx_key, features, |shaped_seg, font| {
2788 if self.origin.x + shaped_seg.x_advance > max_width {
2789 if seg.len() > 2 {
2791 self.push_split_seg(shaped_seg, seg, info, self.word_spacing, text);
2793 } else {
2794 self.push_line_break(true, text);
2795 self.push_glyphs(shaped_seg, self.word_spacing);
2796 self.push_text_seg(seg, info);
2797 }
2798 } else {
2799 self.push_glyphs(shaped_seg, self.word_spacing);
2800 self.push_text_seg(seg, info);
2801 }
2802
2803 self.push_font(font);
2804 });
2805 }
2806 } else if info.kind.is_line_break() {
2807 self.push_text_seg(seg, info);
2808 self.push_line_break(false, text);
2809 } else {
2810 self.push_text_seg(seg, info)
2811 }
2812 }
2813 fn push_emoji_seg(&mut self, shaped_seg: &ShapedSegmentData, font: &Font) {
2814 if !font.face().color_glyphs().is_empty() {
2815 self.out.has_colored_glyphs = true;
2816 }
2817 if (font.face().has_raster_images() || (cfg!(feature = "svg") && font.face().has_svg_images()))
2818 && let Some(ttf) = font.face().raw()
2819 {
2820 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2821 use read_fonts::TableProvider as _;
2822 use skrifa::MetadataProvider as _;
2823
2824 let id = skrifa::GlyphId::new(g.index as _);
2825 let ppm = skrifa::instance::Size::new(font.size().0 as f32);
2826 let glyphs_i = self.out.glyphs.len() - shaped_seg.glyphs.len() + i;
2827 if let Some(img) = ttf.bitmap_strikes().glyph_for_size(ppm, id) {
2828 self.push_glyph_raster(glyphs_i as _, img);
2829 } else if cfg!(feature = "svg")
2830 && let Ok(img) = ttf.svg()
2831 && let Ok(Some(img)) = img.glyph_data(id)
2832 {
2833 self.push_glyph_svg(glyphs_i as _, img);
2834 }
2835 }
2836 }
2837 }
2838
2839 fn push_glyph_raster(&mut self, glyphs_i: u32, img: skrifa::bitmap::BitmapGlyph) {
2840 let size = PxSize::new(Px(img.width as _), Px(img.height as _));
2841
2842 let (data, fmt) = match img.data {
2843 skrifa::bitmap::BitmapData::Bgra(d) => {
2844 let mut bgra = d.to_vec();
2845 for c in bgra.chunks_exact_mut(4) {
2846 let (b, g, r, a) = (c[0], c[1], c[2], c[3]);
2847 let unp = if a == 255 {
2848 [b, g, r]
2849 } else {
2850 [
2851 (b as u32 * 255 / a as u32) as u8,
2852 (g as u32 * 255 / a as u32) as u8,
2853 (r as u32 * 255 / a as u32) as u8,
2854 ]
2855 };
2856 c.copy_from_slice(&unp);
2857 }
2858 let bgra_fmt = ImageDataFormat::Bgra8 {
2859 size,
2860 density: None,
2861 original_color_type: ColorType::BGRA8,
2862 };
2863 (bgra, bgra_fmt)
2864 }
2865 skrifa::bitmap::BitmapData::Png(d) => (d.to_vec(), ImageDataFormat::from("png")),
2866 skrifa::bitmap::BitmapData::Mask(d) => {
2867 let d = match d.decode(img.width, img.height) {
2868 Ok(g) => g,
2869 Err(e) => {
2870 tracing::error!("cannot decode glyph bitmap mask, {e:?}");
2871 vec![0; img.width as usize * img.height as usize]
2872 }
2873 };
2874 (d, ImageDataFormat::A8 { size })
2875 }
2876 };
2877 self.push_glyph_img(glyphs_i, ImageSource::from((data, fmt)));
2878 }
2879
2880 fn push_glyph_svg(&mut self, glyphs_i: u32, img: &[u8]) {
2881 self.push_glyph_img(glyphs_i, ImageSource::from((img, ImageDataFormat::from("svg"))));
2882 }
2883
2884 fn push_glyph_img(&mut self, glyphs_i: u32, source: ImageSource) {
2885 let img = IMAGES.image(source, ImageOptions::cache(), None);
2886
2887 self.out.images.push((glyphs_i, GlyphImage(img)));
2888 }
2889
2890 fn push_last_line(&mut self, text: &SegmentedText) {
2891 let directions = self.finish_current_line_bidi(text);
2892 self.out.lines.0.push(LineRange {
2893 end: self.out.segments.0.len(),
2894 width: self.origin.x,
2895 x_offset: 0.0,
2896 directions,
2897 });
2898
2899 self.out.update_mid_size();
2900 self.out.update_first_last_lines();
2901 self.out.orig_first_line = self.out.first_line.size;
2902 self.out.orig_last_line = self.out.last_line.size;
2903 if self.out.is_inlined && self.out.lines.0.len() > 1 {
2904 self.out.last_line.origin.y += self.out.mid_clear;
2905 }
2906 }
2907
2908 fn push_hyphenate(&mut self, seg: &str, font: &Font, shaped_seg: &ShapedSegmentData, info: TextSegment, text: &SegmentedText) -> bool {
2909 if !matches!(self.hyphens, Hyphens::Auto) {
2910 return false;
2911 }
2912
2913 let split_points = HYPHENATION.hyphenate(&self.lang, seg);
2914 self.push_hyphenate_pt(&split_points, 0, font, shaped_seg, seg, info, text)
2915 }
2916
2917 #[allow(clippy::too_many_arguments)]
2918 fn push_hyphenate_pt(
2919 &mut self,
2920 split_points: &[usize],
2921 split_points_sub: usize,
2922 font: &Font,
2923 shaped_seg: &ShapedSegmentData,
2924 seg: &str,
2925 info: TextSegment,
2926 text: &SegmentedText,
2927 ) -> bool {
2928 if split_points.is_empty() {
2929 return false;
2930 }
2931
2932 let mut end_glyph = 0;
2934 let mut end_point_i = 0;
2935 let max_width = self.actual_max_width();
2936 for (i, point) in split_points.iter().enumerate() {
2937 let mut point = *point - split_points_sub;
2938 let mut width = 0.0;
2939 let mut c = u32::MAX;
2940 let mut gi = 0;
2941 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2943 width = g.point.0;
2944 if g.cluster != c {
2945 if point == 0 {
2947 break;
2948 }
2949 c = g.cluster;
2950 point -= 1;
2951 }
2952 gi = i;
2953 }
2954
2955 if self.origin.x + width + self.hyphen_glyphs.0.x_advance > max_width {
2956 if end_glyph == 0 {
2958 end_glyph = gi + 1;
2960 end_point_i = i + 1;
2961 }
2962 break;
2963 } else {
2964 end_glyph = gi + 1;
2966 end_point_i = i + 1;
2967 }
2968 }
2969
2970 let end_glyph_x = shaped_seg.glyphs[end_glyph].point.0;
2972 let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
2973
2974 if glyphs_a.is_empty() || glyphs_b.is_empty() {
2975 debug_assert!(false, "invalid hyphenation split");
2976 return false;
2977 }
2978 let end_cluster = glyphs_b[0].cluster;
2979 let (seg_a, seg_b) = seg.split_at(end_cluster as usize);
2980
2981 self.push_glyphs(
2982 &ShapedSegmentData {
2983 glyphs: glyphs_a.to_vec(),
2984 x_advance: end_glyph_x,
2985 y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
2986 },
2987 self.word_spacing,
2988 );
2989 self.push_font(font);
2990
2991 self.push_glyphs(&self.hyphen_glyphs.0.clone(), 0.0);
2992 self.push_font(&self.hyphen_glyphs.1.clone());
2993
2994 self.push_text_seg(seg_a, info);
2995
2996 self.push_line_break(true, text);
2997
2998 let mut shaped_seg_b = ShapedSegmentData {
3000 glyphs: glyphs_b.to_vec(),
3001 x_advance: shaped_seg.x_advance - end_glyph_x,
3002 y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3003 };
3004 for g in &mut shaped_seg_b.glyphs {
3005 g.point.0 -= end_glyph_x;
3006 g.cluster -= seg_a.len() as u32;
3007 }
3008
3009 if shaped_seg_b.x_advance > self.actual_max_width() {
3010 if self.push_hyphenate_pt(
3012 &split_points[end_point_i..],
3013 split_points_sub + seg_a.len(),
3014 font,
3015 &shaped_seg_b,
3016 seg_b,
3017 info,
3018 text,
3019 ) {
3020 return true;
3021 }
3022 }
3023
3024 self.push_glyphs(&shaped_seg_b, self.word_spacing);
3026 self.push_text_seg(seg_b, info);
3027 true
3028 }
3029
3030 fn push_glyphs(&mut self, shaped_seg: &ShapedSegmentData, spacing: f32) {
3031 self.out.glyphs.extend(shaped_seg.glyphs.iter().map(|gi| {
3032 let r = GlyphInstance::new(gi.index, euclid::point2(gi.point.0 + self.origin.x, gi.point.1 + self.origin.y));
3033 self.origin.x += spacing;
3034 r
3035 }));
3036 self.out.clusters.extend(shaped_seg.glyphs.iter().map(|gi| gi.cluster));
3037
3038 self.origin.x += shaped_seg.x_advance;
3039 self.origin.y += shaped_seg.y_advance;
3040 }
3041
3042 fn push_line_break(&mut self, soft: bool, text: &SegmentedText) {
3043 if self.out.glyphs.is_empty() && self.allow_first_wrap && soft {
3044 self.out.first_wrapped = true;
3045 } else {
3046 let directions = self.finish_current_line_bidi(text);
3047
3048 self.out.lines.0.push(LineRange {
3049 end: self.out.segments.0.len(),
3050 width: self.origin.x,
3051 x_offset: 0.0,
3052 directions,
3053 });
3054
3055 if self.out.lines.0.len() == 1 {
3056 self.out.first_line = PxRect::from_size(PxSize::new(Px(self.origin.x as i32), Px(self.line_height as i32)));
3057
3058 if !self.out.first_wrapped {
3059 let mid_clear = (self.mid_clear_min - self.line_height).max(0.0).round();
3060 self.origin.y += mid_clear;
3061 self.out.mid_clear = Px(mid_clear as i32);
3062 self.out.mid_offset = mid_clear;
3063 }
3064 }
3065
3066 self.max_line_x = self.origin.x.max(self.max_line_x);
3067
3068 let is_paragraph_break = match self.out.paragraph_break {
3069 ParagraphBreak::None => false,
3070 ParagraphBreak::Line => !soft,
3071 };
3072
3073 self.origin.x = if self.paragraph_indent.1 != is_paragraph_break {
3074 self.paragraph_indent.0
3076 } else {
3077 0.0
3078 };
3079
3080 self.origin.y += if is_paragraph_break {
3081 self.paragraph_spacing
3082 } else {
3083 self.line_height + self.line_spacing
3084 };
3085 }
3086 }
3087
3088 #[must_use]
3089 fn finish_current_line_bidi(&mut self, text: &SegmentedText) -> LayoutDirections {
3090 if self.line_has_rtl {
3091 let seg_start = if self.out.lines.0.is_empty() {
3092 0
3093 } else {
3094 self.out.lines.last().end
3095 };
3096
3097 if self.line_has_ltr {
3098 let line_segs = seg_start..self.out.segments.0.len();
3101
3102 let mut x = 0.0;
3104 for i in text.reorder_line_to_ltr(line_segs) {
3105 let g_range = self.out.segments.glyphs(i);
3106 if g_range.iter().is_empty() {
3107 continue;
3108 }
3109
3110 let glyphs = &mut self.out.glyphs[g_range.iter()];
3111 let offset = x - self.out.segments.0[i].x;
3112 self.out.segments.0[i].x = x;
3113 for g in glyphs {
3114 g.point.x += offset;
3115 }
3116 x += self.out.segments.0[i].advance;
3117 }
3118 } else {
3119 let line_width = self.origin.x;
3121
3122 let mut x = line_width;
3123 for i in seg_start..self.out.segments.0.len() {
3124 x -= self.out.segments.0[i].advance;
3125
3126 let g_range = self.out.segments.glyphs(i);
3127
3128 let glyphs = &mut self.out.glyphs[g_range.iter()];
3129 let offset = x - self.out.segments.0[i].x;
3130 self.out.segments.0[i].x = x;
3131 for g in glyphs {
3132 g.point.x += offset;
3133 }
3134 }
3135 }
3136 }
3137
3138 let mut d = LayoutDirections::empty();
3139 d.set(LayoutDirections::LTR, self.line_has_ltr);
3140 d.set(LayoutDirections::RTL, self.line_has_rtl);
3141
3142 self.line_has_ltr = false;
3143 self.line_has_rtl = false;
3144
3145 d
3146 }
3147
3148 pub fn push_text_seg(&mut self, seg: &str, info: TextSegment) {
3149 let g_len = if let Some(l) = self.out.segments.0.last() {
3150 self.out.glyphs.len() - l.end
3151 } else {
3152 self.out.glyphs.len()
3153 };
3154 if g_len > 0 {
3155 self.line_has_ltr |= info.level.is_ltr();
3156 self.line_has_rtl |= info.level.is_rtl();
3157 }
3158
3159 self.text_seg_end += seg.len();
3160
3161 let is_first_of_line =
3162 (!self.out.lines.0.is_empty() && self.out.lines.last().end == self.out.segments.0.len()) || self.out.segments.0.is_empty();
3163 let x = if is_first_of_line {
3164 0.0
3165 } else {
3166 self.out.segments.0.last().map(|s| s.x + s.advance).unwrap_or(0.0)
3168 };
3169 self.out.segments.0.push(GlyphSegment {
3170 text: TextSegment {
3171 end: self.text_seg_end,
3172 ..info
3173 },
3174 end: self.out.glyphs.len(),
3175 x,
3176 advance: self.origin.x - x,
3177 });
3178 }
3179
3180 pub fn push_split_seg(&mut self, shaped_seg: &ShapedSegmentData, seg: &str, info: TextSegment, spacing: f32, text: &SegmentedText) {
3181 let mut end_glyph = 0;
3182 let mut end_glyph_x = 0.0;
3183 let max_width = self.actual_max_width();
3184 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3185 if self.origin.x + g.point.0 > max_width {
3186 break;
3187 }
3188 end_glyph = i;
3189 end_glyph_x = g.point.0;
3190 }
3191
3192 let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3193
3194 if glyphs_a.is_empty() || glyphs_b.is_empty() {
3195 self.push_line_break(true, text);
3197 self.push_glyphs(shaped_seg, spacing);
3198 self.push_text_seg(seg, info);
3199 } else {
3200 let (seg_a, seg_b) = seg.split_at(glyphs_b[0].cluster as usize);
3201
3202 let shaped_seg_a = ShapedSegmentData {
3203 glyphs: glyphs_a.to_vec(),
3204 x_advance: end_glyph_x,
3205 y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3206 };
3207 self.push_glyphs(&shaped_seg_a, spacing);
3208 self.push_text_seg(seg_a, info);
3209 self.push_line_break(true, text);
3210
3211 let mut shaped_seg_b = ShapedSegmentData {
3212 glyphs: glyphs_b.to_vec(),
3213 x_advance: shaped_seg.x_advance - end_glyph_x,
3214 y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3215 };
3216 for g in &mut shaped_seg_b.glyphs {
3217 g.point.0 -= shaped_seg_a.x_advance;
3218 g.cluster -= seg_a.len() as u32;
3219 }
3220
3221 if shaped_seg_b.x_advance <= max_width {
3222 self.push_glyphs(&shaped_seg_b, spacing);
3223 self.push_text_seg(seg_b, info);
3224 } else {
3225 self.push_split_seg(&shaped_seg_b, seg_b, info, spacing, text);
3226 }
3227 }
3228 }
3229
3230 fn push_font(&mut self, font: &Font) {
3231 if let Some(last) = self.out.fonts.0.last_mut() {
3232 if &last.font == font {
3233 last.end = self.out.glyphs.len();
3234 return;
3235 } else if last.end == self.out.glyphs.len() {
3236 return;
3237 }
3238 }
3239 self.out.fonts.0.push(FontRange {
3240 font: font.clone(),
3241 end: self.out.glyphs.len(),
3242 })
3243 }
3244}
3245
3246bitflags! {
3247 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3249 #[serde(transparent)]
3250 pub struct LayoutDirections: u8 {
3251 const LTR = 1;
3253 const RTL = 2;
3255 const BIDI = Self::LTR.bits() | Self::RTL.bits();
3259 }
3260}
3261
3262#[derive(Clone, Copy)]
3264pub struct ShapedLine<'a> {
3265 text: &'a ShapedText,
3266 seg_range: IndexRange,
3268 index: usize,
3269 width: Px,
3270}
3271impl fmt::Debug for ShapedLine<'_> {
3272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3273 f.debug_struct("ShapedLine")
3274 .field("seg_range", &self.seg_range)
3275 .field("index", &self.index)
3276 .field("width", &self.width)
3277 .finish_non_exhaustive()
3278 }
3279}
3280impl<'a> ShapedLine<'a> {
3281 pub fn height(&self) -> Px {
3283 if self.index == 0 {
3284 self.text.first_line.height()
3285 } else if self.index == self.text.lines.0.len() - 1 {
3286 self.text.last_line.height()
3287 } else {
3288 self.text.line_height
3289 }
3290 }
3291
3292 pub fn width(&self) -> Px {
3294 if self.index == 0 {
3295 self.text.first_line.width()
3296 } else if self.index == self.text.lines.0.len() - 1 {
3297 self.text.last_line.width()
3298 } else {
3299 self.width
3300 }
3301 }
3302
3303 pub fn rect(&self) -> PxRect {
3305 if self.index == 0 {
3306 return self.text.first_line;
3307 }
3308 if self.index == self.text.lines.0.len() - 1 {
3309 return self.text.last_line;
3310 }
3311
3312 let size = PxSize::new(self.width, self.text.line_height);
3313 let origin = PxPoint::new(
3314 Px(self.text.lines.0[self.index].x_offset as i32),
3315 self.text.line_height * Px((self.index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear,
3316 );
3317 PxRect::new(origin, size)
3318 }
3319
3320 pub fn original_size(&self) -> PxSize {
3327 if self.index == 0 {
3328 return self.text.orig_first_line;
3329 }
3330 if self.index == self.text.lines.0.len() - 1 {
3331 return self.text.orig_last_line;
3332 }
3333 PxSize::new(self.width, self.text.line_height)
3334 }
3335
3336 pub fn is_paragraph_start(&self) -> bool {
3342 match self.text.paragraph_break {
3343 ParagraphBreak::None => self.index == 0,
3344 ParagraphBreak::Line => !self.started_by_wrap(),
3345 }
3346 }
3347
3348 pub fn is_paragraph_end(&self) -> bool {
3354 match self.text.paragraph_break {
3355 ParagraphBreak::None => self.index == self.text.lines.0.len() - 1,
3356 ParagraphBreak::Line => !self.ended_by_wrap(),
3357 }
3358 }
3359
3360 pub fn overline(&self) -> (PxPoint, Px) {
3362 self.decoration_line(self.text.overline)
3363 }
3364
3365 pub fn strikethrough(&self) -> (PxPoint, Px) {
3367 self.decoration_line(self.text.strikethrough)
3368 }
3369
3370 pub fn underline(&self) -> (PxPoint, Px) {
3376 self.decoration_line(self.text.underline)
3377 }
3378
3379 pub fn underline_descent(&self) -> (PxPoint, Px) {
3385 self.decoration_line(self.text.underline_descent)
3386 }
3387
3388 pub fn underline_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3394 MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline()))
3395 }
3396
3397 pub fn underline_descent_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3403 MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline_descent()))
3404 }
3405
3406 pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3412 MergingLineIter::new(self.segs().flat_map(move |s| s.underline_skip_glyphs(thickness)))
3413 }
3414
3415 pub fn underline_skip_glyphs_and_spaces(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3421 MergingLineIter::new(
3422 self.segs()
3423 .filter(|s| s.kind().is_word())
3424 .flat_map(move |s| s.underline_skip_glyphs(thickness)),
3425 )
3426 }
3427
3428 fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3429 let r = self.rect();
3430 let y = r.max_y() - bottom_up_offset;
3431 (PxPoint::new(r.origin.x, y), self.width)
3432 }
3433
3434 fn segments(&self) -> &'a [GlyphSegment] {
3435 &self.text.segments.0[self.seg_range.iter()]
3436 }
3437
3438 pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a {
3445 let r = self.glyphs_range();
3446 self.text.glyphs_range(r)
3447 }
3448
3449 pub fn glyphs_with_x_advance(&self) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + 'a)> + 'a {
3451 self.segs().flat_map(|s| s.glyphs_with_x_advance())
3452 }
3453
3454 fn glyphs_range(&self) -> IndexRange {
3455 self.text.segments.glyphs_range(self.seg_range)
3456 }
3457
3458 pub fn segs(&self) -> impl DoubleEndedIterator<Item = ShapedSegment<'a>> + ExactSizeIterator + use<'a> {
3460 let text = self.text;
3461 let line_index = self.index;
3462 self.seg_range.iter().map(move |i| ShapedSegment {
3463 text,
3464 line_index,
3465 index: i,
3466 })
3467 }
3468
3469 pub fn segs_len(&self) -> usize {
3471 self.seg_range.len()
3472 }
3473
3474 pub fn seg(&self, seg_idx: usize) -> Option<ShapedSegment<'_>> {
3478 if self.seg_range.len() > seg_idx {
3479 Some(ShapedSegment {
3480 text: self.text,
3481 line_index: self.index,
3482 index: seg_idx + self.seg_range.start(),
3483 })
3484 } else {
3485 None
3486 }
3487 }
3488
3489 pub fn started_by_wrap(&self) -> bool {
3495 self.index > 0 && {
3496 let prev_line = self.text.lines.segs(self.index - 1);
3497 self.text.segments.0[prev_line.iter()]
3498 .last()
3499 .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3500 .unwrap() }
3502 }
3503
3504 pub fn ended_by_wrap(&self) -> bool {
3510 self.index < self.text.lines.0.len() - 1
3512 && self
3513 .segments()
3514 .last()
3515 .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3516 .unwrap() }
3518
3519 pub fn actual_line_start(&self) -> Self {
3523 let mut r = *self;
3524 while r.started_by_wrap() {
3525 r = r.text.line(r.index - 1).unwrap();
3526 }
3527 r
3528 }
3529
3530 pub fn actual_line_end(&self) -> Self {
3534 let mut r = *self;
3535 while r.ended_by_wrap() {
3536 r = r.text.line(r.index + 1).unwrap();
3537 }
3538 r
3539 }
3540
3541 pub fn text_range(&self) -> ops::Range<usize> {
3543 let start = self.seg_range.start();
3544 let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3545 let end = self.seg_range.end();
3546 let end = if end == 0 { 0 } else { self.text.segments.0[end - 1].text.end };
3547
3548 start..end
3549 }
3550
3551 pub fn text_caret_range(&self) -> ops::Range<usize> {
3556 let start = self.seg_range.start();
3557 let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3558 let end = self.seg_range.end();
3559 let end = if end == 0 {
3560 0
3561 } else if self.seg_range.start() == end {
3562 start
3563 } else {
3564 let seg = &self.text.segments.0[end - 1];
3565 if !matches!(seg.text.kind, TextSegmentKind::LineBreak) {
3566 seg.text.end
3567 } else {
3568 if end == 1 { 0 } else { self.text.segments.0[end - 2].text.end }
3570 }
3571 };
3572
3573 start..end
3574 }
3575
3576 pub fn actual_text_range(&self) -> ops::Range<usize> {
3578 let start = self.actual_line_start().text_range().start;
3579 let end = self.actual_line_end().text_range().end;
3580 start..end
3581 }
3582
3583 pub fn actual_text_caret_range(&self) -> ops::Range<usize> {
3585 let start = self.actual_line_start().text_range().start;
3586 let end = self.actual_line_end().text_caret_range().end;
3587 start..end
3588 }
3589
3590 pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
3594 let r = self.text_range();
3595
3596 let start = r.start.min(full_text.len());
3597 let end = r.end.min(full_text.len());
3598
3599 &full_text[start..end]
3600 }
3601
3602 pub fn nearest_seg(&self, x: Px) -> Option<ShapedSegment<'a>> {
3604 let mut min = None;
3605 let mut min_dist = Px::MAX;
3606 for seg in self.segs() {
3607 let (seg_x, width) = seg.x_width();
3608 if x >= seg_x {
3609 let seg_max_x = seg_x + width;
3610 if x < seg_max_x {
3611 return Some(seg);
3612 }
3613 }
3614 let dist = (x - seg_x).abs();
3615 if min_dist > dist {
3616 min = Some(seg);
3617 min_dist = dist;
3618 }
3619 }
3620 min
3621 }
3622
3623 pub fn index(&self) -> usize {
3625 self.index
3626 }
3627
3628 pub fn directions(&self) -> LayoutDirections {
3630 self.text.lines.0[self.index].directions
3631 }
3632}
3633
3634struct MergingLineIter<I> {
3636 iter: I,
3637 line: Option<(PxPoint, Px)>,
3638}
3639impl<I> MergingLineIter<I> {
3640 pub fn new(iter: I) -> Self {
3641 MergingLineIter { iter, line: None }
3642 }
3643}
3644impl<I: Iterator<Item = (PxPoint, Px)>> Iterator for MergingLineIter<I> {
3645 type Item = I::Item;
3646
3647 fn next(&mut self) -> Option<Self::Item> {
3648 loop {
3649 match self.iter.next() {
3650 Some(line) => {
3651 if let Some(prev_line) = &mut self.line {
3652 fn min_x((origin, _width): (PxPoint, Px)) -> Px {
3653 origin.x
3654 }
3655 fn max_x((origin, width): (PxPoint, Px)) -> Px {
3656 origin.x + width
3657 }
3658
3659 if prev_line.0.y == line.0.y && min_x(*prev_line) <= max_x(line) && max_x(*prev_line) >= min_x(line) {
3660 let x = min_x(*prev_line).min(min_x(line));
3661 prev_line.1 = max_x(*prev_line).max(max_x(line)) - x;
3662 prev_line.0.x = x;
3663 } else {
3664 let cut = mem::replace(prev_line, line);
3665 return Some(cut);
3666 }
3667 } else {
3668 self.line = Some(line);
3669 continue;
3670 }
3671 }
3672 None => return self.line.take(),
3673 }
3674 }
3675 }
3676}
3677
3678struct MergingRectIter<I> {
3679 iter: I,
3680 rect: Option<PxBox>,
3681}
3682impl<I> MergingRectIter<I> {
3683 pub fn new(iter: I) -> Self {
3684 MergingRectIter { iter, rect: None }
3685 }
3686}
3687impl<I: Iterator<Item = PxRect>> Iterator for MergingRectIter<I> {
3688 type Item = I::Item;
3689
3690 fn next(&mut self) -> Option<Self::Item> {
3691 loop {
3692 match self.iter.next() {
3693 Some(rect) => {
3694 let rect = rect.to_box2d();
3695 if let Some(prev_rect) = &mut self.rect {
3696 if prev_rect.min.y == rect.min.y
3697 && prev_rect.max.y == rect.max.y
3698 && prev_rect.min.x <= rect.max.x
3699 && prev_rect.max.x >= rect.min.x
3700 {
3701 prev_rect.min.x = prev_rect.min.x.min(rect.min.x);
3702 prev_rect.max.x = prev_rect.max.x.max(rect.max.x);
3703 continue;
3704 } else {
3705 let cut = mem::replace(prev_rect, rect);
3706 return Some(cut.to_rect());
3707 }
3708 } else {
3709 self.rect = Some(rect);
3710 continue;
3711 }
3712 }
3713 None => return self.rect.take().map(|r| r.to_rect()),
3714 }
3715 }
3716 }
3717}
3718
3719#[derive(Clone, Copy)]
3721pub struct ShapedSegment<'a> {
3722 text: &'a ShapedText,
3723 line_index: usize,
3724 index: usize,
3725}
3726impl fmt::Debug for ShapedSegment<'_> {
3727 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3728 f.debug_struct("ShapedSegment")
3729 .field("line_index", &self.line_index)
3730 .field("index", &self.index)
3731 .finish_non_exhaustive()
3732 }
3733}
3734impl<'a> ShapedSegment<'a> {
3735 pub fn kind(&self) -> TextSegmentKind {
3737 self.text.segments.0[self.index].text.kind
3738 }
3739
3740 pub fn level(&self) -> BidiLevel {
3742 self.text.segments.0[self.index].text.level
3743 }
3744
3745 pub fn direction(&self) -> LayoutDirection {
3747 self.text.segments.0[self.index].text.direction()
3748 }
3749
3750 pub fn has_last_glyph(&self) -> bool {
3752 let seg_glyphs = self.text.segments.glyphs(self.index);
3753 let s = self.text.lines.segs(self.line_index);
3754 let line_glyphs = self.text.segments.glyphs_range(s);
3755 seg_glyphs.end() == line_glyphs.end()
3756 }
3757
3758 fn glyphs_range(&self) -> IndexRange {
3759 self.text.segments.glyphs(self.index)
3760 }
3761
3762 pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> {
3775 let r = self.glyphs_range();
3776 self.text.glyphs_range(r)
3777 }
3778
3779 pub fn glyph(&self, index: usize) -> Option<(&'a Font, GlyphInstance)> {
3781 let mut r = self.glyphs_range();
3782 r.0 += index;
3783 self.text.glyphs_range(r).next().map(|(f, g)| (f, g[0]))
3784 }
3785
3786 pub fn clusters(&self) -> &[u32] {
3794 let r = self.glyphs_range();
3795 self.text.clusters_range(r)
3796 }
3797
3798 pub fn clusters_count(&self) -> usize {
3802 let mut c = u32::MAX;
3803 let mut count = 0;
3804 for &i in self.clusters() {
3805 if i != c {
3806 c = i;
3807 count += 1;
3808 }
3809 }
3810 count
3811 }
3812
3813 pub fn ligature_segs_count(&self) -> usize {
3816 let range = self.glyphs_range();
3817 if range.iter().is_empty() {
3818 0
3819 } else {
3820 self.text.segments.0[self.index + 1..]
3821 .iter()
3822 .filter(|s| s.end == range.end())
3823 .count()
3824 }
3825 }
3826
3827 pub fn glyphs_with_x_advance(
3831 &self,
3832 ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + use<'a>)> + use<'a> {
3833 let r = self.glyphs_range();
3834 self.text.seg_glyphs_with_x_advance(self.index, r)
3835 }
3836
3837 pub fn cluster_glyphs_with_x_advance(
3841 &self,
3842 ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (u32, &'a [GlyphInstance], f32)> + use<'a>)> + use<'a> {
3843 let r = self.glyphs_range();
3844 self.text.seg_cluster_glyphs_with_x_advance(self.index, r)
3845 }
3846
3847 pub fn x_width(&self) -> (Px, Px) {
3849 let IndexRange(start, end) = self.glyphs_range();
3850
3851 let is_line_break = start == end && matches!(self.kind(), TextSegmentKind::LineBreak);
3852
3853 let start_x = match self.direction() {
3854 LayoutDirection::LTR => {
3855 if is_line_break || start == self.text.glyphs.len() {
3856 let x = self.text.lines.x_offset(self.line_index);
3857 let w = self.text.lines.width(self.line_index);
3858 return (Px((x + w) as i32), Px(0));
3859 }
3860 self.text.glyphs[start].point.x
3861 }
3862 LayoutDirection::RTL => {
3863 if is_line_break || start == self.text.glyphs.len() {
3864 let x = self.text.lines.x_offset(self.line_index);
3865 return (Px(x as i32), Px(0));
3866 }
3867
3868 self.text.glyphs[start..end]
3869 .iter()
3870 .map(|g| g.point.x)
3871 .min_by(f32::total_cmp)
3872 .unwrap_or(0.0)
3873 }
3874 };
3875
3876 (Px(start_x.floor() as i32), Px(self.advance().ceil() as i32))
3877 }
3878
3879 pub fn advance(&self) -> f32 {
3881 self.text.segments.0[self.index].advance
3882 }
3883
3884 pub fn rect(&self) -> PxRect {
3886 let (x, width) = self.x_width();
3887 let size = PxSize::new(width, self.text.line_height);
3888
3889 let y = if self.line_index == 0 {
3890 self.text.first_line.origin.y
3891 } else if self.line_index == self.text.lines.0.len() - 1 {
3892 self.text.last_line.origin.y
3893 } else {
3894 self.text.line_height * Px((self.line_index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear
3895 };
3896 PxRect::new(PxPoint::new(x, y), size)
3897 }
3898
3899 pub fn overflow_char_glyph(&self, max_width_px: f32) -> Option<(usize, usize)> {
3901 if self.advance() > max_width_px {
3902 match self.direction() {
3903 LayoutDirection::LTR => {
3904 let mut x = 0.0;
3905 let mut g = 0;
3906 for (_, c) in self.cluster_glyphs_with_x_advance() {
3907 for (cluster, glyphs, advance) in c {
3908 x += advance;
3909 if x > max_width_px {
3910 return Some((cluster as usize, g));
3911 }
3912 g += glyphs.len();
3913 }
3914 }
3915 }
3916 LayoutDirection::RTL => {
3917 let mut g = 0;
3918 let mut rev = smallvec::SmallVec::<[_; 10]>::new();
3919 for (_, c) in self.cluster_glyphs_with_x_advance() {
3920 for (cluster, glyphs, advance) in c {
3921 rev.push((cluster, g, advance));
3922 g += glyphs.len();
3923 }
3924 }
3925
3926 let mut x = 0.0;
3927 for (c, g, advance) in rev.into_iter().rev() {
3928 x += advance;
3929 if x > max_width_px {
3930 return Some((c as usize, g));
3931 }
3932 }
3933 }
3934 }
3935 }
3936 None
3937 }
3938
3939 pub fn inline_info(&self) -> InlineSegmentInfo {
3941 let (x, width) = self.x_width();
3942 InlineSegmentInfo::new(x, width)
3943 }
3944
3945 fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3946 let (x, width) = self.x_width();
3947 let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - bottom_up_offset;
3948 (PxPoint::new(x, y), width)
3949 }
3950
3951 pub fn overline(&self) -> (PxPoint, Px) {
3953 self.decoration_line(self.text.overline)
3954 }
3955
3956 pub fn strikethrough(&self) -> (PxPoint, Px) {
3958 self.decoration_line(self.text.strikethrough)
3959 }
3960
3961 pub fn underline(&self) -> (PxPoint, Px) {
3967 self.decoration_line(self.text.underline)
3968 }
3969
3970 pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + use<'a> {
3974 let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - self.text.underline;
3975 let (x, _) = self.x_width();
3976
3977 let line_y = -(self.text.baseline - self.text.underline).0 as f32;
3978 let line_y_range = (line_y, line_y - thickness.0 as f32);
3979
3980 let padding = (thickness.0 as f32).clamp(1.0, (self.text.fonts.font(0).size().0 as f32 * 0.2).max(1.0));
3982
3983 struct UnderlineSkipGlyphs<'a, I, J> {
3985 line_y_range: (f32, f32),
3986 y: Px,
3987 padding: f32,
3988 min_width: Px,
3989
3990 iter: I,
3991 resume: Option<(&'a Font, J)>,
3992 x: f32,
3993 width: f32,
3994 }
3995 impl<I, J> UnderlineSkipGlyphs<'_, I, J> {
3996 fn line(&self) -> Option<(PxPoint, Px)> {
3997 fn f32_to_px(px: f32) -> Px {
3998 Px(px.round() as i32)
3999 }
4000 let r = (PxPoint::new(f32_to_px(self.x), self.y), f32_to_px(self.width));
4001 if r.1 >= self.min_width { Some(r) } else { None }
4002 }
4003 }
4004 impl<'a, I, J> Iterator for UnderlineSkipGlyphs<'a, I, J>
4005 where
4006 I: Iterator<Item = (&'a Font, J)>,
4007 J: Iterator<Item = (GlyphInstance, f32)>,
4008 {
4009 type Item = (PxPoint, Px);
4010
4011 fn next(&mut self) -> Option<Self::Item> {
4012 loop {
4013 let continuation = self.resume.take().or_else(|| self.iter.next());
4014 if let Some((font, mut glyphs_with_adv)) = continuation {
4015 for (g, a) in &mut glyphs_with_adv {
4016 if let Some((ex_start, ex_end)) = font.h_line_hits(g.index, self.line_y_range) {
4017 self.width += ex_start - self.padding;
4018 let r = self.line();
4019 self.x += self.width + self.padding + ex_end + self.padding;
4020 self.width = a - (ex_start + ex_end) - self.padding;
4021
4022 if r.is_some() {
4023 self.resume = Some((font, glyphs_with_adv));
4024 return r;
4025 }
4026 } else {
4027 self.width += a;
4028 }
4030 }
4031 } else {
4032 let r = self.line();
4033 self.width = 0.0;
4034 return r;
4035 }
4036 }
4037 }
4038 }
4039 UnderlineSkipGlyphs {
4040 line_y_range,
4041 y,
4042 padding,
4043 min_width: Px((padding / 2.0).max(1.0).ceil() as i32),
4044
4045 iter: self.glyphs_with_x_advance(),
4046 resume: None,
4047 x: x.0 as f32,
4048 width: 0.0,
4049 }
4050 }
4051
4052 pub fn underline_descent(&self) -> (PxPoint, Px) {
4058 self.decoration_line(self.text.underline_descent)
4059 }
4060
4061 pub fn text_range(&self) -> ops::Range<usize> {
4063 self.text_start()..self.text_end()
4064 }
4065
4066 pub fn text_start(&self) -> usize {
4068 if self.index == 0 {
4069 0
4070 } else {
4071 self.text.segments.0[self.index - 1].text.end
4072 }
4073 }
4074
4075 pub fn text_end(&self) -> usize {
4077 self.text.segments.0[self.index].text.end
4078 }
4079
4080 pub fn text_glyph_range(&self, glyph_range: impl ops::RangeBounds<usize>) -> ops::Range<usize> {
4084 let included_start = match glyph_range.start_bound() {
4085 ops::Bound::Included(i) => Some(*i),
4086 ops::Bound::Excluded(i) => Some(*i + 1),
4087 ops::Bound::Unbounded => None,
4088 };
4089 let excluded_end = match glyph_range.end_bound() {
4090 ops::Bound::Included(i) => Some(*i - 1),
4091 ops::Bound::Excluded(i) => Some(*i),
4092 ops::Bound::Unbounded => None,
4093 };
4094
4095 let glyph_range_start = self.glyphs_range().start();
4096 let glyph_to_char = |g| self.text.clusters[glyph_range_start + g] as usize;
4097
4098 match (included_start, excluded_end) {
4099 (None, None) => IndexRange(0, self.text_range().len()),
4100 (None, Some(end)) => IndexRange(0, glyph_to_char(end)),
4101 (Some(start), None) => IndexRange(glyph_to_char(start), self.text_range().len()),
4102 (Some(start), Some(end)) => IndexRange(glyph_to_char(start), glyph_to_char(end)),
4103 }
4104 .iter()
4105 }
4106
4107 pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
4111 let r = self.text_range();
4112 let start = r.start.min(full_text.len());
4113 let end = r.end.min(full_text.len());
4114 &full_text[start..end]
4115 }
4116
4117 pub fn nearest_char_index(&self, x: Px, full_text: &str) -> usize {
4119 let txt_range = self.text_range();
4120 let is_rtl = self.direction().is_rtl();
4121 let x = x.0 as f32;
4122
4123 let seg_clusters = self.clusters();
4124
4125 for (font, clusters) in self.cluster_glyphs_with_x_advance() {
4126 for (cluster, glyphs, advance) in clusters {
4127 let found = x < glyphs[0].point.x || glyphs[0].point.x + advance > x;
4128 if !found {
4129 continue;
4130 }
4131 let cluster_i = seg_clusters.iter().position(|&c| c == cluster).unwrap();
4132
4133 let char_a = txt_range.start + cluster as usize;
4134 let char_b = if is_rtl {
4135 if cluster_i == 0 {
4136 txt_range.end
4137 } else {
4138 txt_range.start + seg_clusters[cluster_i - 1] as usize
4139 }
4140 } else {
4141 let next_cluster = cluster_i + glyphs.len();
4142 if next_cluster == seg_clusters.len() {
4143 txt_range.end
4144 } else {
4145 txt_range.start + seg_clusters[next_cluster] as usize
4146 }
4147 };
4148
4149 if char_b - char_a > 1 && glyphs.len() == 1 {
4150 let text = &full_text[char_a..char_b];
4153
4154 let mut lig_parts = smallvec::SmallVec::<[u16; 6]>::new_const();
4155 for (i, _) in unicode_segmentation::UnicodeSegmentation::grapheme_indices(text, true) {
4156 lig_parts.push(i as u16);
4157 }
4158
4159 if lig_parts.len() > 1 {
4160 let x = x - glyphs[0].point.x;
4163
4164 let mut split = true;
4165 for (i, font_caret) in font.ligature_caret_offsets(glyphs[0].index).enumerate() {
4166 if i == lig_parts.len() {
4167 break;
4168 }
4169 split = false;
4170
4171 if font_caret > x {
4172 return char_a + lig_parts[i] as usize;
4174 }
4175 }
4176 if split {
4177 let lig_part = advance / lig_parts.len() as f32;
4179 let mut lig_x = lig_part;
4180 if is_rtl {
4181 for c in lig_parts.into_iter().rev() {
4182 if lig_x > x {
4183 return char_a + c as usize;
4185 }
4186 lig_x += lig_part;
4187 }
4188 } else {
4189 for c in lig_parts {
4190 if lig_x > x {
4191 return char_a + c as usize;
4192 }
4193 lig_x += lig_part;
4194 }
4195 }
4196 }
4197 }
4198 }
4199 let middle_x = glyphs[0].point.x + advance / 2.0;
4202
4203 return if is_rtl {
4204 if x <= middle_x { char_b } else { char_a }
4205 } else if x <= middle_x {
4206 char_a
4207 } else {
4208 char_b
4209 };
4210 }
4211 }
4212
4213 let mut start = is_rtl;
4214 if matches!(self.kind(), TextSegmentKind::LineBreak) {
4215 start = !start;
4216 }
4217 if start { txt_range.start } else { txt_range.end }
4218 }
4219
4220 pub fn index(&self) -> usize {
4222 self.index - self.text.lines.segs(self.line_index).start()
4223 }
4224}
4225
4226const WORD_CACHE_MAX_LEN: usize = 32;
4227const WORD_CACHE_MAX_ENTRIES: usize = 10_000;
4228
4229#[derive(Hash, PartialEq, Eq)]
4230pub(super) struct WordCacheKey<S> {
4231 string: S,
4232 ctx_key: WordContextKey,
4233}
4234#[derive(Hash)]
4235struct WordCacheKeyRef<'a, S> {
4236 string: &'a S,
4237 ctx_key: &'a WordContextKey,
4238}
4239
4240#[derive(Hash, PartialEq, Eq, Clone)]
4241pub(super) struct WordContextKey {
4242 lang: unic_langid::subtags::Language,
4243 script: Option<unic_langid::subtags::Script>,
4244 direction: LayoutDirection,
4245 features: Box<[usize]>,
4246}
4247impl WordContextKey {
4248 pub fn new(lang: &Lang, direction: LayoutDirection, font_features: &RFontFeatures) -> Self {
4249 let is_64 = mem::size_of::<usize>() == mem::size_of::<u64>();
4250
4251 let mut features = vec![];
4252
4253 if !font_features.is_empty() {
4254 features.reserve(font_features.len() * if is_64 { 3 } else { 4 });
4255 for feature in font_features {
4256 let tag = u32::from_be_bytes(feature.tag.to_be_bytes());
4257 if is_64 {
4258 let mut h = tag as u64;
4259 h |= (feature.value as u64) << 32;
4260 features.push(h as usize);
4261 } else {
4262 features.push(tag as usize);
4263 features.push(feature.value as usize);
4264 }
4265
4266 features.push(feature.start as usize);
4267 features.push(feature.end as usize);
4268 }
4269 }
4270
4271 WordContextKey {
4272 lang: lang.language,
4273 script: lang.script,
4274 direction,
4275 features: features.into_boxed_slice(),
4276 }
4277 }
4278
4279 pub fn harfbuzz_lang(&self) -> Option<harfrust::Language> {
4280 self.lang.as_str().parse().ok()
4281 }
4282
4283 pub fn harfbuzz_script(&self) -> Option<harfrust::Script> {
4284 let t: u32 = self.script?.into();
4285 let t = t.to_le_bytes(); harfrust::Script::from_iso15924_tag(skrifa::Tag::new(&[t[0], t[1], t[2], t[3]]))
4287 }
4288
4289 pub fn harfbuzz_direction(&self) -> harfrust::Direction {
4290 into_harf_direction(self.direction)
4291 }
4292}
4293
4294#[derive(Debug, Clone, Default)]
4295pub(super) struct ShapedSegmentData {
4296 glyphs: Vec<ShapedGlyph>,
4297 x_advance: f32,
4298 y_advance: f32,
4299}
4300#[derive(Debug, Clone, Copy)]
4301struct ShapedGlyph {
4302 index: u32,
4304 cluster: u32,
4306 point: (f32, f32),
4307}
4308
4309impl Font {
4310 fn buffer_segment(&self, segment: &str, key: &WordContextKey) -> harfrust::UnicodeBuffer {
4311 let mut buffer = harfrust::UnicodeBuffer::new();
4312 buffer.set_direction(key.harfbuzz_direction());
4313 buffer.set_cluster_level(harfrust::BufferClusterLevel::MonotoneCharacters);
4314
4315 if let Some(lang) = key.harfbuzz_lang() {
4316 buffer.set_language(lang);
4317 }
4318 if let Some(script) = key.harfbuzz_script() {
4319 buffer.set_script(script);
4320 }
4321
4322 buffer.push_str(segment);
4323 buffer
4324 }
4325
4326 fn shape_segment_no_cache(&self, seg: &str, key: &WordContextKey, features: &[harfrust::Feature]) -> ShapedSegmentData {
4327 let buffer = if let Some(font) = self.face().raw()
4328 && let Some(shaper_data) = &self.0.shaper_cache
4329 {
4330 let buffer = self.buffer_segment(seg, key);
4331 let shaper_builder = shaper_data.shaper(&font);
4332 let shaper = shaper_builder.build();
4333 shaper.shape(buffer, harfrust::ShapeOptions::new().features(features))
4334 } else {
4335 return ShapedSegmentData {
4336 glyphs: vec![],
4337 x_advance: 0.0,
4338 y_advance: 0.0,
4339 };
4340 };
4341
4342 let size_scale = self.0.size.0 as f32 / self.0.metrics.units_per_em as f32;
4343 let to_layout = |p: i32| p as f32 * size_scale;
4344
4345 let mut w_x_advance = 0.0;
4346 let mut w_y_advance = 0.0;
4347
4348 let glyphs: Vec<_> = buffer
4349 .glyph_infos()
4350 .iter()
4351 .zip(buffer.glyph_positions())
4352 .map(|(i, p)| {
4353 let x_offset = to_layout(p.x_offset);
4354 let y_offset = -to_layout(p.y_offset);
4355 let x_advance = to_layout(p.x_advance);
4356 let y_advance = to_layout(p.y_advance);
4357
4358 let point = (w_x_advance + x_offset, w_y_advance + y_offset);
4359 w_x_advance += x_advance;
4360 w_y_advance += y_advance;
4361
4362 ShapedGlyph {
4363 index: i.glyph_id,
4364 cluster: i.cluster,
4365 point,
4366 }
4367 })
4368 .collect();
4369
4370 ShapedSegmentData {
4371 glyphs,
4372 x_advance: w_x_advance,
4373 y_advance: w_y_advance,
4374 }
4375 }
4376
4377 fn shape_segment<R>(
4378 &self,
4379 seg: &str,
4380 word_ctx_key: &WordContextKey,
4381 features: &[harfrust::Feature],
4382 out: impl FnOnce(&ShapedSegmentData) -> R,
4383 ) -> R {
4384 if !(1..=WORD_CACHE_MAX_LEN).contains(&seg.len()) || self.face().is_empty() {
4385 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4386 out(&seg)
4387 } else if let Some(small) = Self::to_small_word(seg) {
4388 let cache = self.0.small_word_cache.read();
4390
4391 let hash = cache.hasher().hash_one(WordCacheKeyRef {
4392 string: &small,
4393 ctx_key: word_ctx_key,
4394 });
4395
4396 if let Some((_, seg)) = cache
4397 .raw_entry()
4398 .from_hash(hash, |e| e.string == small && &e.ctx_key == word_ctx_key)
4399 {
4400 return out(seg);
4401 }
4402 drop(cache);
4403
4404 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4406 let key = WordCacheKey {
4407 string: small,
4408 ctx_key: word_ctx_key.clone(),
4409 };
4410 let r = out(&seg);
4411 let mut cache = self.0.small_word_cache.write();
4412 if cache.len() > WORD_CACHE_MAX_ENTRIES {
4413 cache.clear();
4414 }
4415 cache.insert(key, seg);
4416 r
4417 } else {
4418 let cache = self.0.word_cache.read();
4420
4421 let hash = cache.hasher().hash_one(WordCacheKeyRef {
4422 string: &seg,
4423 ctx_key: word_ctx_key,
4424 });
4425
4426 if let Some((_, seg)) = cache
4427 .raw_entry()
4428 .from_hash(hash, |e| e.string.as_str() == seg && &e.ctx_key == word_ctx_key)
4429 {
4430 return out(seg);
4431 }
4432 drop(cache);
4433
4434 let string = seg.to_owned();
4436 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4437 let key = WordCacheKey {
4438 string,
4439 ctx_key: word_ctx_key.clone(),
4440 };
4441 let r = out(&seg);
4442 let mut cache = self.0.word_cache.write();
4443 if cache.len() > WORD_CACHE_MAX_ENTRIES {
4444 cache.clear();
4445 }
4446 cache.insert(key, seg);
4447 r
4448 }
4449 }
4450
4451 pub fn space_index(&self) -> GlyphIndex {
4453 self.shape_space().0
4454 }
4455
4456 pub fn space_x_advance(&self) -> Px {
4458 self.shape_space().1
4459 }
4460
4461 fn shape_space(&self) -> (GlyphIndex, Px) {
4462 let mut id = 0;
4463 let mut adv = 0.0;
4464 self.shape_segment(
4465 " ",
4466 &WordContextKey {
4467 lang: unic_langid::subtags::Language::from_bytes(b"und").unwrap(),
4468 script: None,
4469 direction: LayoutDirection::LTR,
4470 features: Box::new([]),
4471 },
4472 &[],
4473 |r| {
4474 id = r.glyphs.last().map(|g| g.index).unwrap_or(0);
4475 adv = r.x_advance;
4476 },
4477 );
4478 (id, Px(adv as _))
4479 }
4480
4481 pub fn shape_text(self: &Font, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4483 ShapedTextBuilder::shape_text(std::slice::from_ref(self), text, config)
4484 }
4485
4486 pub fn outline(&self, glyph_id: GlyphIndex, sink: &mut impl OutlineSink) -> Option<PxRect> {
4490 struct AdapterSink<'a, S> {
4491 sink: &'a mut S,
4492 bounds: euclid::Box2D<f32, Px>,
4493 }
4494 impl<S> AdapterSink<'_, S> {
4495 fn point(&mut self, x: f32, y: f32) -> euclid::Point2D<f32, Px> {
4496 let p = euclid::point2(x, y);
4497 self.bounds.min = self.bounds.min.min(p);
4498 self.bounds.max = self.bounds.max.max(p);
4499 p
4500 }
4501 }
4502 impl<S: OutlineSink> skrifa::outline::OutlinePen for AdapterSink<'_, S> {
4503 fn move_to(&mut self, x: f32, y: f32) {
4504 let p = self.point(x, y);
4505 self.sink.move_to(p)
4506 }
4507
4508 fn line_to(&mut self, x: f32, y: f32) {
4509 let p = self.point(x, y);
4510 self.sink.line_to(p)
4511 }
4512
4513 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
4514 let ctrl = self.point(x1, y1);
4515 let to = self.point(x, y);
4516 self.sink.quadratic_curve_to(ctrl, to)
4517 }
4518
4519 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
4520 let l_from = self.point(x1, y1);
4521 let l_to = self.point(x2, y2);
4522 let to = self.point(x, y);
4523 self.sink.cubic_curve_to((l_from, l_to), to)
4524 }
4525
4526 fn close(&mut self) {
4527 self.sink.close()
4528 }
4529 }
4530 let size = skrifa::instance::Size::new(self.size().0 as f32);
4531
4532 let f = self.face().raw()?;
4533 let o = f.outline_glyphs().get(skrifa::GlyphId::new(glyph_id))?;
4534 let mut sink = AdapterSink {
4535 sink,
4536 bounds: euclid::Box2D::zero(),
4537 };
4538 o.draw(
4539 skrifa::outline::DrawSettings::unhinted(size, skrifa::instance::LocationRef::default()),
4540 &mut sink,
4541 )
4542 .ok()?;
4543 Some(sink.bounds.to_rect().cast())
4544 }
4545
4546 pub fn h_line_hits(&self, glyph_id: GlyphIndex, line_y_range: (f32, f32)) -> Option<(f32, f32)> {
4556 struct InterceptsSink {
4562 start: Option<euclid::Point2D<f32, Px>>,
4563 current: euclid::Point2D<f32, Px>,
4564 under: (bool, bool),
4565
4566 line_y_range: (f32, f32),
4567 hit: Option<(f32, f32)>,
4568 }
4569 impl OutlineSink for InterceptsSink {
4570 fn move_to(&mut self, to: euclid::Point2D<f32, Px>) {
4571 self.start = Some(to);
4572 self.current = to;
4573 self.under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4574 }
4575
4576 fn line_to(&mut self, to: euclid::Point2D<f32, Px>) {
4577 let under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4578
4579 if self.under != under || under == (true, false) {
4580 self.under = under;
4582
4583 let (x0, x1) = if self.current.x < to.x {
4584 (self.current.x, to.x)
4585 } else {
4586 (to.x, self.current.x)
4587 };
4588 if let Some((min, max)) = &mut self.hit {
4589 *min = min.min(x0);
4590 *max = max.max(x1);
4591 } else {
4592 self.hit = Some((x0, x1));
4593 }
4594 }
4595
4596 self.current = to;
4597 self.under = under;
4598 }
4599
4600 fn quadratic_curve_to(&mut self, _: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>) {
4601 self.line_to(to);
4602 }
4603
4604 fn cubic_curve_to(&mut self, _: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>) {
4605 self.line_to(to);
4606 }
4607
4608 fn close(&mut self) {
4609 if let Some(s) = self.start.take()
4610 && s != self.current
4611 {
4612 self.line_to(s);
4613 }
4614 }
4615 }
4616 let mut sink = InterceptsSink {
4617 start: None,
4618 current: euclid::point2(0.0, 0.0),
4619 under: (false, false),
4620
4621 line_y_range,
4622 hit: None,
4623 };
4624 self.outline(glyph_id, &mut sink)?;
4625
4626 sink.hit.map(|(a, b)| (a, b - a))
4627 }
4628}
4629
4630pub trait OutlineSink {
4634 fn move_to(&mut self, to: euclid::Point2D<f32, Px>);
4636 fn line_to(&mut self, to: euclid::Point2D<f32, Px>);
4638 fn quadratic_curve_to(&mut self, ctrl: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>);
4640 fn cubic_curve_to(&mut self, ctrl: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>);
4644 fn close(&mut self);
4646}
4647
4648impl FontList {
4649 pub fn shape_text(&self, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4651 ShapedTextBuilder::shape_text(self, text, config)
4652 }
4653}
4654
4655#[derive(Clone, Copy)]
4657struct IndexRange(pub usize, pub usize);
4658impl fmt::Debug for IndexRange {
4659 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4660 write!(f, "{}..{}", self.0, self.1)
4661 }
4662}
4663impl IntoIterator for IndexRange {
4664 type Item = usize;
4665
4666 type IntoIter = std::ops::Range<usize>;
4667
4668 fn into_iter(self) -> Self::IntoIter {
4669 self.iter()
4670 }
4671}
4672impl From<IndexRange> for std::ops::Range<usize> {
4673 fn from(c: IndexRange) -> Self {
4674 c.iter()
4675 }
4676}
4677impl From<std::ops::Range<usize>> for IndexRange {
4678 fn from(r: std::ops::Range<usize>) -> Self {
4679 IndexRange(r.start, r.end)
4680 }
4681}
4682impl IndexRange {
4683 pub fn from_bounds(bounds: impl ops::RangeBounds<usize>) -> Self {
4684 let start = match bounds.start_bound() {
4686 ops::Bound::Included(&i) => i,
4687 ops::Bound::Excluded(&i) => i + 1,
4688 ops::Bound::Unbounded => 0,
4689 };
4690 let end = match bounds.end_bound() {
4691 ops::Bound::Included(&i) => i + 1,
4692 ops::Bound::Excluded(&i) => i,
4693 ops::Bound::Unbounded => 0,
4694 };
4695 Self(start, end)
4696 }
4697
4698 pub fn iter(self) -> std::ops::Range<usize> {
4700 self.0..self.1
4701 }
4702
4703 pub fn start(self) -> usize {
4705 self.0
4706 }
4707
4708 pub fn end(self) -> usize {
4710 self.1
4711 }
4712
4713 pub fn len(self) -> usize {
4715 self.end() - self.start()
4716 }
4717}
4718impl std::ops::RangeBounds<usize> for IndexRange {
4719 fn start_bound(&self) -> std::ops::Bound<&usize> {
4720 std::ops::Bound::Included(&self.0)
4721 }
4722
4723 fn end_bound(&self) -> std::ops::Bound<&usize> {
4724 std::ops::Bound::Excluded(&self.1)
4725 }
4726}
4727
4728pub fn f32_cmp(a: &f32, b: &f32) -> std::cmp::Ordering {
4730 a.partial_cmp(b).unwrap()
4731}
4732
4733fn into_harf_direction(d: LayoutDirection) -> harfrust::Direction {
4734 match d {
4735 LayoutDirection::LTR => harfrust::Direction::LeftToRight,
4736 LayoutDirection::RTL => harfrust::Direction::RightToLeft,
4737 }
4738}
4739
4740#[cfg(test)]
4741mod tests {
4742 use crate::{
4743 FONTS, Font, FontName, FontStretch, FontStyle, FontWeight, ParagraphBreak, SegmentedText, TextReshapingArgs, TextShapingArgs,
4744 WordContextKey,
4745 };
4746 use zng_app::APP;
4747 use zng_ext_l10n::lang;
4748 use zng_layout::{
4749 context::LayoutDirection,
4750 unit::{Px, PxConstraints2d},
4751 };
4752
4753 fn test_font() -> Font {
4754 let mut app = APP.minimal().run_headless(false);
4755 let font = app
4756 .run_test(async {
4757 FONTS
4758 .normal(&FontName::sans_serif(), &lang!(und))
4759 .wait_rsp()
4760 .await
4761 .unwrap()
4762 .sized(Px(20), vec![])
4763 })
4764 .unwrap();
4765 drop(app);
4766 font
4767 }
4768
4769 #[test]
4770 fn set_line_spacing() {
4771 let text = "0\n1\n2\n3\n4";
4772 test_line_spacing(text, Px(20), Px(0));
4773 test_line_spacing(text, Px(0), Px(20));
4774 test_line_spacing(text, Px(4), Px(6));
4775 test_line_spacing(text, Px(4), Px(4));
4776 test_line_spacing("a line\nanother\nand another", Px(20), Px(0));
4777 test_line_spacing("", Px(20), Px(0));
4778 test_line_spacing("a line", Px(20), Px(0));
4779 }
4780 fn test_line_spacing(text: &'static str, from: Px, to: Px) {
4781 let font = test_font();
4782 let mut config = TextShapingArgs {
4783 line_height: Px(40),
4784 line_spacing: from,
4785 ..Default::default()
4786 };
4787
4788 let text = SegmentedText::new(text, LayoutDirection::LTR);
4789 let mut test = font.shape_text(&text, &config);
4790
4791 config.line_spacing = to;
4792 let expected = font.shape_text(&text, &config);
4793
4794 assert_eq!(from, test.line_spacing());
4795 test.reshape_lines(&TextReshapingArgs {
4796 constraints: PxConstraints2d::new_fill_size(test.align_size()),
4797 inline_constraints: None,
4798 align: test.align(),
4799 overflow_align: test.overflow_align(),
4800 direction: test.direction(),
4801 line_height: test.line_height(),
4802 line_spacing: to,
4803 paragraph_spacing: Px(0),
4804 paragraph_indent: (Px(0), false),
4805 paragraph_break: ParagraphBreak::None,
4806 });
4807 assert_eq!(to, test.line_spacing());
4808
4809 for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4810 assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4811 }
4812
4813 assert_eq!(test.size(), expected.size());
4814 }
4815
4816 #[test]
4817 fn set_line_height() {
4818 let text = "0\n1\n2\n3\n4";
4819 test_line_height(text, Px(20), Px(20));
4820 test_line_height(text, Px(20), Px(10));
4821 test_line_height(text, Px(10), Px(20));
4822 test_line_height("a line\nanother\nand another", Px(20), Px(10));
4823 test_line_height("", Px(20), Px(10));
4824 test_line_height("a line", Px(20), Px(10));
4825 }
4826 fn test_line_height(text: &'static str, from: Px, to: Px) {
4827 let font = test_font();
4828 let mut config = TextShapingArgs {
4829 line_height: from,
4830 line_spacing: Px(20),
4831 ..Default::default()
4832 };
4833
4834 let text = SegmentedText::new(text, LayoutDirection::LTR);
4835 let mut test = font.shape_text(&text, &config);
4836
4837 config.line_height = to;
4838 let expected = font.shape_text(&text, &config);
4839
4840 assert_eq!(from, test.line_height());
4841 test.reshape_lines(&TextReshapingArgs {
4842 constraints: PxConstraints2d::new_fill_size(test.align_size()),
4843 inline_constraints: None,
4844 align: test.align(),
4845 overflow_align: test.overflow_align(),
4846 direction: test.direction(),
4847 line_height: to,
4848 line_spacing: test.line_spacing(),
4849 paragraph_spacing: Px(0),
4850 paragraph_indent: (Px(0), false),
4851 paragraph_break: ParagraphBreak::None,
4852 });
4853 assert_eq!(to, test.line_height());
4854
4855 for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4856 assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4857 }
4858
4859 assert_eq!(test.size(), expected.size());
4860 }
4861
4862 #[test]
4863 fn font_fallback_issue() {
4864 let mut app = APP.minimal().run_headless(false);
4865 app.run_test(async {
4866 let font = FONTS
4867 .list(
4868 &[FontName::new("Consolas"), FontName::monospace()],
4869 FontStyle::Normal,
4870 FontWeight::NORMAL,
4871 FontStretch::NORMAL,
4872 &lang!(und),
4873 )
4874 .wait_rsp()
4875 .await
4876 .sized(Px(20), vec![]);
4877
4878 let config = TextShapingArgs::default();
4879
4880 let txt_seg = SegmentedText::new("النص ثنائي الاتجاه (بالإنجليزية:Bi", LayoutDirection::RTL);
4881 let txt_shape = font.shape_text(&txt_seg, &config);
4882
4883 let _ok = (txt_seg, txt_shape);
4884 })
4885 .unwrap()
4886 }
4887
4888 #[test]
4889 fn cluster_is_byte() {
4890 let font = test_font();
4891
4892 let data = font.shape_segment_no_cache("£a", &WordContextKey::new(&lang!("en-US"), LayoutDirection::LTR, &vec![]), &[]);
4893
4894 for ((i, _), g) in "£a".char_indices().zip(&data.glyphs) {
4895 assert_eq!(i as u32, g.cluster);
4896 }
4897 }
4898}