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