1use std::{
2 cmp, fmt,
3 hash::{BuildHasher, Hash},
4 mem, ops,
5};
6
7use zng_app::widget::info::InlineSegmentInfo;
8use zng_ext_image::{ColorType, IMAGES, ImageDataFormat, ImageOptions, ImageSource, ImageVar};
9use zng_ext_l10n::{Lang, lang};
10use zng_layout::{
11 context::{InlineConstraintsLayout, InlineConstraintsMeasure, InlineSegmentPos, LayoutDirection, TextSegmentKind},
12 unit::{Align, Factor2d, FactorUnits, Px, PxBox, PxConstraints2d, PxPoint, PxRect, PxSize, about_eq, euclid},
13};
14use zng_txt::Txt;
15use zng_view_api::font::{GlyphIndex, GlyphInstance};
16
17use crate::{
18 BidiLevel, CaretIndex, Font, FontList, HYPHENATION, Hyphens, Justify, LineBreak, ParagraphBreak, SegmentedText, TextSegment, WordBreak,
19 font_features::RFontFeatures,
20};
21
22#[derive(Clone, Copy, PartialEq, Debug)]
24#[non_exhaustive]
25pub enum GlyphLoadingError {
26 NoSuchGlyph,
28 PlatformError,
30}
31impl std::error::Error for GlyphLoadingError {}
32impl fmt::Display for GlyphLoadingError {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 use GlyphLoadingError::*;
35 match self {
36 NoSuchGlyph => write!(f, "no such glyph"),
37 PlatformError => write!(f, "platform error"),
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44#[non_exhaustive]
45pub struct TextShapingArgs {
46 pub letter_spacing: Px,
48
49 pub word_spacing: Px,
51
52 pub line_height: Px,
58
59 pub line_spacing: Px,
61
62 pub paragraph_spacing: Px,
64
65 pub paragraph_indent: (Px, bool),
69
70 pub lang: Lang,
72
73 pub direction: LayoutDirection,
75
76 pub ignore_ligatures: bool,
78
79 pub disable_kerning: bool,
81
82 pub tab_x_advance: Px,
84
85 pub inline_constraints: Option<InlineConstraintsMeasure>,
87
88 pub font_features: RFontFeatures,
90
91 pub max_width: Px,
95
96 pub paragraph_break: ParagraphBreak,
98
99 pub line_break: LineBreak,
101
102 pub word_break: WordBreak,
106
107 pub hyphens: Hyphens,
109
110 pub hyphen_char: Txt,
112
113 pub obscuring_char: Option<char>,
115}
116impl Default for TextShapingArgs {
117 fn default() -> Self {
118 TextShapingArgs {
119 letter_spacing: Px(0),
120 word_spacing: Px(0),
121 line_height: Px(0),
122 line_spacing: Px(0),
123 paragraph_spacing: Px(0),
124 paragraph_indent: (Px(0), false),
125 lang: lang!(und),
126 direction: LayoutDirection::LTR,
127 ignore_ligatures: false,
128 disable_kerning: false,
129 tab_x_advance: Px(0),
130 inline_constraints: None,
131 font_features: RFontFeatures::default(),
132 max_width: Px::MAX,
133 paragraph_break: Default::default(),
134 line_break: Default::default(),
135 word_break: Default::default(),
136 hyphens: Default::default(),
137 hyphen_char: Txt::from_char('-'),
138 obscuring_char: None,
139 }
140 }
141}
142
143#[derive(Debug, Clone, Default)]
145#[non_exhaustive]
146pub struct TextReshapingArgs {
147 pub constraints: PxConstraints2d,
149 pub inline_constraints: Option<InlineConstraintsLayout>,
151 pub align: Align,
153 pub overflow_align: Align,
155 pub direction: LayoutDirection,
157
158 pub line_height: Px,
160 pub line_spacing: Px,
162 pub paragraph_spacing: Px,
164 pub paragraph_indent: (Px, bool),
168 pub paragraph_break: ParagraphBreak,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq)]
174struct LineRange {
175 end: usize,
178 width: f32,
180 x_offset: f32,
182 directions: LayoutDirections,
183}
184
185#[derive(Clone)]
187struct FontRange {
188 font: Font,
189 end: usize,
191}
192impl PartialEq for FontRange {
193 fn eq(&self, other: &Self) -> bool {
194 self.font == other.font && self.end == other.end
195 }
196}
197impl fmt::Debug for FontRange {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 f.debug_struct("FontInfo")
200 .field("font", &self.font.face().display_name().name())
201 .field("end", &self.end)
202 .finish()
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq)]
207struct GlyphSegment {
208 pub text: TextSegment,
209 pub end: usize,
211 pub x: f32,
213 pub advance: f32,
215}
216
217#[derive(Debug, Default, Clone, PartialEq)]
219struct GlyphSegmentVec(Vec<GlyphSegment>);
220impl GlyphSegmentVec {
221 fn glyphs(&self, index: usize) -> IndexRange {
223 let start = if index == 0 { 0 } else { self.0[index - 1].end };
224 let end = self.0[index].end;
225 IndexRange(start, end)
226 }
227
228 fn glyphs_range(&self, range: IndexRange) -> IndexRange {
230 let IndexRange(start, end) = range;
231
232 if end == 0 {
233 return IndexRange(0, 0);
234 }
235
236 let start = if start == 0 { 0 } else { self.0[start - 1].end };
237 let end = self.0[end - 1].end;
238
239 IndexRange(start, end)
240 }
241}
242
243#[derive(Debug, Default, Clone, PartialEq)]
245struct LineRangeVec(Vec<LineRange>);
246impl LineRangeVec {
247 fn segs(&self, index: usize) -> IndexRange {
249 let end = self.0[index].end;
250 let start = if index == 0 { 0 } else { self.0[index - 1].end };
251 IndexRange(start, end)
252 }
253
254 fn width(&self, index: usize) -> f32 {
256 self.0[index].width
257 }
258
259 fn x_offset(&self, index: usize) -> f32 {
261 self.0[index].x_offset
262 }
263
264 fn iter_segs(&self) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
266 self.iter_segs_skip(0)
267 }
268
269 fn iter_segs_skip(&self, start_line: usize) -> impl Iterator<Item = (f32, IndexRange)> + '_ {
271 let mut start = self.segs(start_line).start();
272 self.0[start_line..].iter().map(move |l| {
273 let r = IndexRange(start, l.end);
274 start = l.end;
275 (l.width, r)
276 })
277 }
278
279 fn is_multi(&self) -> bool {
281 self.0.len() > 1
282 }
283
284 fn first_mut(&mut self) -> &mut LineRange {
285 &mut self.0[0]
286 }
287
288 fn last(&self) -> LineRange {
289 self.0[self.0.len() - 1]
290 }
291
292 fn last_mut(&mut self) -> &mut LineRange {
293 let l = self.0.len() - 1;
294 &mut self.0[l]
295 }
296}
297
298#[derive(Debug, Default, Clone, PartialEq)]
300struct FontRangeVec(Vec<FontRange>);
301impl FontRangeVec {
302 fn iter_glyphs(&self) -> impl Iterator<Item = (&Font, IndexRange)> + '_ {
304 let mut start = 0;
305 self.0.iter().map(move |f| {
306 let r = IndexRange(start, f.end);
307 start = f.end;
308 (&f.font, r)
309 })
310 }
311
312 fn iter_glyphs_clip(&self, glyphs_range: IndexRange) -> impl Iterator<Item = (&Font, IndexRange)> + '_ {
314 let mut start = glyphs_range.start();
315 let end = glyphs_range.end();
316 let first_font = self.0.iter().position(|f| f.end > start).unwrap_or(self.0.len().saturating_sub(1));
317
318 self.0[first_font..].iter().map_while(move |f| {
319 let i = f.end.min(end);
320
321 if i > start {
322 let r = IndexRange(start, i);
323 start = i;
324 Some((&f.font, r))
325 } else {
326 None
327 }
328 })
329 }
330
331 fn font(&self, index: usize) -> &Font {
333 &self.0[index].font
334 }
335}
336
337#[derive(Clone)]
338struct GlyphImage(ImageVar);
339impl PartialEq for GlyphImage {
340 fn eq(&self, other: &Self) -> bool {
341 self.0.var_eq(&other.0)
342 }
343}
344impl fmt::Debug for GlyphImage {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 write!(f, "GlyphImage(_)")
347 }
348}
349
350#[derive(Debug, Clone, PartialEq)]
354pub struct ShapedText {
355 glyphs: Vec<GlyphInstance>,
357 clusters: Vec<u32>,
359 segments: GlyphSegmentVec,
361 lines: LineRangeVec,
362 fonts: FontRangeVec,
363 images: Vec<(u32, GlyphImage)>,
365
366 line_height: Px,
367 line_spacing: Px,
368 paragraph_spacing: Px,
369 paragraph_break: ParagraphBreak,
370
371 orig_line_height: Px,
372 orig_line_spacing: Px,
373 orig_first_line: PxSize,
374 orig_last_line: PxSize,
375
376 baseline: Px,
378 overline: Px,
379 strikethrough: Px,
380 underline: Px,
381 underline_descent: Px,
382
383 mid_offset: f32,
385 align_size: PxSize,
386 align: Align,
387 justify: Justify, justified: Vec<f32>, overflow_align: Align,
390 direction: LayoutDirection,
391
392 is_inlined: bool,
394 first_wrapped: bool,
395 first_line: PxRect,
396 mid_clear: Px,
397 mid_size: PxSize,
398 last_line: PxRect,
399
400 has_colored_glyphs: bool,
401}
402
403pub enum ShapedColoredGlyphs<'a> {
405 Normal(&'a [GlyphInstance]),
407 Colored {
409 point: euclid::Point2D<f32, Px>,
411 base_glyph: GlyphIndex,
415
416 glyphs: super::ColorGlyph<'a>,
418 },
419}
420
421pub enum ShapedImageGlyphs<'a> {
423 Normal(&'a [GlyphInstance]),
425 Image {
427 rect: euclid::Rect<f32, Px>,
431 base_glyph: GlyphIndex,
435 img: &'a ImageVar,
437 },
438}
439
440impl ShapedText {
441 pub fn new(font: &Font) -> Self {
443 font.shape_text(&SegmentedText::new("", LayoutDirection::LTR), &TextShapingArgs::default())
444 }
445
446 pub fn glyphs(&self) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
455 self.fonts.iter_glyphs().map(move |(f, r)| (f, &self.glyphs[r.iter()]))
456 }
457
458 pub fn glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
464 self.glyphs_slice_impl(IndexRange::from_bounds(range))
465 }
466 fn glyphs_slice_impl(&self, range: IndexRange) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
467 self.fonts.iter_glyphs_clip(range).map(move |(f, r)| (f, &self.glyphs[r.iter()]))
468 }
469
470 pub fn has_colored_glyphs(&self) -> bool {
472 self.has_colored_glyphs
473 }
474
475 pub fn has_images(&self) -> bool {
477 !self.images.is_empty()
478 }
479
480 pub fn colored_glyphs(&self) -> impl Iterator<Item = (&Font, ShapedColoredGlyphs<'_>)> {
482 ColoredGlyphsIter {
483 glyphs: self.glyphs(),
484 maybe_colored: None,
485 }
486 }
487
488 pub fn colored_glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, ShapedColoredGlyphs<'_>)> {
490 ColoredGlyphsIter {
491 glyphs: self.glyphs_slice_impl(IndexRange::from_bounds(range)),
492 maybe_colored: None,
493 }
494 }
495
496 pub fn image_glyphs(&self) -> impl Iterator<Item = (&Font, ShapedImageGlyphs<'_>)> {
498 ImageGlyphsIter {
499 glyphs: self.glyphs(),
500 glyphs_i: 0,
501 images: &self.images,
502 maybe_img: None,
503 }
504 }
505
506 pub fn image_glyphs_slice(&self, range: impl ops::RangeBounds<usize>) -> impl Iterator<Item = (&Font, ShapedImageGlyphs<'_>)> {
508 let range = IndexRange::from_bounds(range);
509 ImageGlyphsIter {
510 glyphs_i: range.start() as _,
511 glyphs: self.glyphs_slice_impl(range),
512 images: &self.images,
513 maybe_img: None,
514 }
515 }
516
517 fn glyphs_range(&self, range: IndexRange) -> impl Iterator<Item = (&Font, &[GlyphInstance])> {
519 self.fonts.iter_glyphs_clip(range).map(|(f, r)| (f, &self.glyphs[r.iter()]))
520 }
521
522 fn clusters_range(&self, range: IndexRange) -> &[u32] {
525 &self.clusters[range.iter()]
526 }
527
528 fn seg_glyphs_with_x_advance(
529 &self,
530 seg_idx: usize,
531 glyphs_range: IndexRange,
532 ) -> impl Iterator<Item = (&Font, impl Iterator<Item = (GlyphInstance, f32)> + '_)> + '_ {
533 let mut gi = glyphs_range.start();
534 let seg_x = if gi < self.glyphs.len() { self.glyphs[gi].point.x } else { 0.0 };
535 let seg_advance = self.segments.0[seg_idx].advance;
536 self.glyphs_range(glyphs_range).map(move |(font, glyphs)| {
537 let g_adv = glyphs.iter().map(move |g| {
538 gi += 1;
539
540 let adv = if gi == glyphs_range.end() {
541 (seg_x + seg_advance) - g.point.x
542 } else {
543 self.glyphs[gi].point.x - g.point.x
544 };
545 (*g, adv)
546 });
547
548 (font, g_adv)
549 })
550 }
551
552 fn seg_cluster_glyphs_with_x_advance(
553 &self,
554 seg_idx: usize,
555 glyphs_range: IndexRange,
556 ) -> impl Iterator<Item = (&Font, impl Iterator<Item = (u32, &[GlyphInstance], f32)>)> {
557 let mut gi = glyphs_range.start();
558 let seg_x = if gi < self.glyphs.len() { self.glyphs[gi].point.x } else { 0.0 };
559 let seg_advance = self.segments.0[seg_idx].advance;
560 let seg_clusters = self.clusters_range(glyphs_range);
561 let mut cluster_i = 0;
562
563 self.glyphs_range(glyphs_range).map(move |(font, glyphs)| {
564 let clusters = &seg_clusters[cluster_i..cluster_i + glyphs.len()];
565 cluster_i += glyphs.len();
566
567 struct Iter<'a> {
568 clusters: &'a [u32],
569 glyphs: &'a [GlyphInstance],
570 }
571 impl<'a> Iterator for Iter<'a> {
572 type Item = (u32, &'a [GlyphInstance]);
573
574 fn next(&mut self) -> Option<Self::Item> {
575 if let Some(c) = self.clusters.first() {
576 let end = self.clusters.iter().rposition(|rc| rc == c).unwrap();
577 let glyphs = &self.glyphs[..=end];
578 self.clusters = &self.clusters[end + 1..];
579 self.glyphs = &self.glyphs[end + 1..];
580 Some((*c, glyphs))
581 } else {
582 None
583 }
584 }
585 }
586
587 let g_adv = Iter { clusters, glyphs }.map(move |(c, gs)| {
588 gi += gs.len();
589
590 let adv = if gi == glyphs_range.end() {
591 (seg_x + seg_advance) - gs[0].point.x
592 } else {
593 self.glyphs[gi].point.x - gs[0].point.x
594 };
595 (c, gs, adv)
596 });
597
598 (font, g_adv)
599 })
600 }
601
602 pub fn size(&self) -> PxSize {
605 let first_width = self.first_line.origin.x.abs() + self.first_line.size.width;
606 let last_width = self.last_line.origin.x.abs() + self.last_line.size.width;
607 self.mid_size()
608 .max(PxSize::new(first_width.max(last_width), self.last_line.max_y()))
609 }
610
611 pub fn block_size(&self) -> PxSize {
613 if self.lines.0.is_empty() {
614 PxSize::zero()
615 } else if self.lines.0.len() == 1 {
616 self.first_line.size
617 } else {
618 let mut s = PxSize::new(
619 self.first_line.size.width.max(self.last_line.size.width),
620 self.first_line.size.height + self.line_spacing + self.last_line.size.height,
621 );
622 if self.lines.0.len() > 2 {
623 s.width = s.width.max(self.mid_size.width);
624 s.height += self.mid_size.height + self.line_spacing;
625 }
626 s
627 }
628 }
629
630 pub fn overflow_line(&self, max_height: Px) -> Option<ShapedLine<'_>> {
633 let mut y = self.first_line.max_y();
634 if y > max_height {
635 self.line(0)
636 } else if self.lines.0.len() > 1 {
637 let mid_lines = self.lines.0.len() - 2;
638 for i in 0..=mid_lines {
639 y += self.line_spacing;
640 y += self.line_height;
641 if y > max_height {
642 return self.line(i + 1);
643 }
644 }
645
646 if self.last_line.max_y() > max_height {
647 self.line(self.lines.0.len() - 1)
648 } else {
649 None
650 }
651 } else {
652 None
653 }
654 }
655
656 fn update_mid_size(&mut self) {
657 self.mid_size = if self.lines.0.len() <= 2 {
658 PxSize::zero()
659 } else {
660 let mid_lines = &self.lines.0[1..self.lines.0.len() - 1];
661 PxSize::new(
662 Px(mid_lines.iter().map(|l| l.width).max_by(f32_cmp).unwrap_or_default().ceil() as i32),
663 Px(mid_lines.len() as i32) * self.line_height + Px((mid_lines.len() - 1) as i32) * self.line_spacing,
664 )
665 };
666 }
667
668 fn update_first_last_lines(&mut self) {
669 if self.lines.0.is_empty() {
670 self.first_line = PxRect::zero();
671 self.last_line = PxRect::zero();
672 self.align_size = PxSize::zero();
673 } else {
674 self.first_line = PxRect::from_size(PxSize::new(Px(self.lines.first_mut().width.ceil() as i32), self.line_height));
675
676 if self.lines.0.len() > 1 {
677 self.last_line.size = PxSize::new(Px(self.lines.last().width.ceil() as i32), self.line_height);
678 self.last_line.origin = PxPoint::new(Px(0), self.first_line.max_y() + self.line_spacing);
679 if self.lines.0.len() > 2 {
680 self.last_line.origin.y += self.mid_size.height + self.line_spacing;
681 }
682 } else {
683 self.last_line = self.first_line;
684 }
685 self.align_size = self.block_size();
686 }
687 }
688
689 pub fn mid_size(&self) -> PxSize {
691 self.mid_size
692 }
693
694 pub fn is_inlined(&self) -> bool {
701 self.is_inlined
702 }
703
704 pub fn align(&self) -> Align {
708 self.align
709 }
710
711 pub fn justify_mode(&self) -> Option<Justify> {
717 match self.justify {
718 Justify::Auto => None,
719 m => {
720 debug_assert!(self.align.is_fill_x());
721 Some(m)
722 }
723 }
724 }
725
726 pub fn overflow_align(&self) -> Align {
732 self.overflow_align
733 }
734
735 pub fn align_size(&self) -> PxSize {
739 self.align_size
740 }
741
742 pub fn direction(&self) -> LayoutDirection {
747 self.direction
748 }
749
750 pub fn mid_clear(&self) -> Px {
753 self.mid_clear
754 }
755
756 pub fn reshape_lines(&mut self, args: &TextReshapingArgs) {
768 self.clear_justify_impl(args.align.is_fill_x());
769 self.reshape_line_height_and_spacing(args.line_height, args.line_spacing);
770
771 let is_inlined = args.inline_constraints.is_some();
772
773 let align_x = args.align.x(args.direction);
774 let align_y = if is_inlined { 0.fct() } else { args.align.y() };
775 let overflow_align_x = args.overflow_align.x(args.direction);
776 let overflow_align_y = if is_inlined { 0.fct() } else { args.overflow_align.y() };
777
778 let (first, mid, last, first_segs, last_segs) = if let Some(l) = &args.inline_constraints {
779 (l.first, l.mid_clear, l.last, &*l.first_segs, &*l.last_segs)
780 } else {
781 let block_size = self.block_size();
783 let align_size = args.constraints.fill_size_or(block_size);
784
785 let mut first = PxRect::from_size(self.line(0).map(|l| l.rect().size).unwrap_or_default());
786 let mut last = PxRect::from_size(
787 self.line(self.lines_len().saturating_sub(1))
788 .map(|l| l.rect().size)
789 .unwrap_or_default(),
790 );
791 last.origin.y = block_size.height - last.size.height;
792
793 match first.size.width.cmp(&align_size.width) {
794 cmp::Ordering::Less => first.origin.x = (align_size.width - first.size.width) * align_x,
795 cmp::Ordering::Equal => {}
796 cmp::Ordering::Greater => first.origin.x = (align_size.width - first.size.width) * overflow_align_x,
797 }
798 match last.size.width.cmp(&align_size.width) {
799 cmp::Ordering::Less => last.origin.x = (align_size.width - last.size.width) * align_x,
800 cmp::Ordering::Equal => {}
801 cmp::Ordering::Greater => last.origin.x = (align_size.width - last.size.width) * overflow_align_x,
802 }
803
804 match block_size.height.cmp(&align_size.height) {
805 cmp::Ordering::Less => {
806 let align_y = (align_size.height - block_size.height) * align_y;
807 first.origin.y += align_y;
808 last.origin.y += align_y;
809 }
810 cmp::Ordering::Equal => {}
811 cmp::Ordering::Greater => {
812 let align_y = (align_size.height - block_size.height) * overflow_align_y;
813 first.origin.y += align_y;
814 last.origin.y += align_y;
815 }
816 }
817
818 static EMPTY: Vec<InlineSegmentPos> = vec![];
819 (first, Px(0), last, &EMPTY, &EMPTY)
820 };
821
822 if !self.lines.0.is_empty() {
823 if self.first_line != first {
824 let first_offset = (first.origin - self.first_line.origin).cast::<f32>().cast_unit();
825
826 let first_range = self.lines.segs(0);
827 let first_glyphs = self.segments.glyphs_range(first_range);
828
829 for g in &mut self.glyphs[first_glyphs.iter()] {
830 g.point += first_offset;
831 }
832
833 let first_line = self.lines.first_mut();
834 first_line.x_offset = first.origin.x.0 as f32;
835 }
839 if !first_segs.is_empty() {
840 let first_range = self.lines.segs(0);
842 if first_range.len() == first_segs.len() {
843 for i in first_range.iter() {
844 let seg_offset = first_segs[i].x - self.segments.0[i].x;
845 let glyphs = self.segments.glyphs(i);
846 for g in &mut self.glyphs[glyphs.iter()] {
847 g.point.x += seg_offset;
848 }
849 self.segments.0[i].x = first_segs[i].x;
850 }
851 } else {
852 #[cfg(debug_assertions)]
853 {
854 tracing::error!("expected {} segments in `first_segs`, was {}", first_range.len(), first_segs.len());
855 }
856 }
857 }
858 }
859
860 if self.lines.0.len() > 1 {
861 if self.last_line != last {
862 let last_offset = (last.origin - self.last_line.origin).cast::<f32>().cast_unit();
865
866 let last_range = self.lines.segs(self.lines.0.len() - 1);
867 let last_glyphs = self.segments.glyphs_range(last_range);
868
869 for g in &mut self.glyphs[last_glyphs.iter()] {
870 g.point += last_offset;
871 }
872
873 let last_line = self.lines.last_mut();
874 last_line.x_offset = last.origin.x.0 as f32;
875 }
878 if !last_segs.is_empty() {
879 let last_range = self.lines.segs(self.lines.0.len() - 1);
881
882 if last_range.len() == last_segs.len() {
883 for i in last_range.iter() {
884 let li = i - last_range.start();
885
886 let seg_offset = last_segs[li].x - self.segments.0[i].x;
887 let glyphs = self.segments.glyphs(i);
888 for g in &mut self.glyphs[glyphs.iter()] {
889 g.point.x += seg_offset;
890 }
891 self.segments.0[i].x = last_segs[li].x;
892 }
893 } else {
894 #[cfg(debug_assertions)]
895 {
896 tracing::error!("expected {} segments in `last_segs`, was {}", last_range.len(), last_segs.len());
897 }
898 }
899 }
900 }
901
902 self.first_line = first;
903 self.last_line = last;
904
905 let block_size = self.block_size();
906 let align_size = args.constraints.fill_size_or(block_size);
907
908 if self.lines.0.len() > 2 {
909 let mid_offset = euclid::vec2::<f32, Px>(
912 0.0,
913 match block_size.height.cmp(&align_size.height) {
914 cmp::Ordering::Less => (align_size.height - block_size.height).0 as f32 * align_y + mid.0 as f32,
915 cmp::Ordering::Equal => mid.0 as f32,
916 cmp::Ordering::Greater => (align_size.height - block_size.height).0 as f32 * overflow_align_y + mid.0 as f32,
917 },
918 );
919 let y_transform = mid_offset.y - self.mid_offset;
920 let align_width = align_size.width.0 as f32;
921
922 let skip_last = self.lines.0.len() - 2;
923 let mut line_start = self.lines.0[0].end;
924 for line in &mut self.lines.0[1..=skip_last] {
925 let x_offset = if line.width < align_width {
926 (align_width - line.width) * align_x
927 } else {
928 (align_width - line.width) * overflow_align_x
929 };
930 let x_transform = x_offset - line.x_offset;
931
932 let glyphs = self.segments.glyphs_range(IndexRange(line_start, line.end));
933 for g in &mut self.glyphs[glyphs.iter()] {
934 g.point.x += x_transform;
935 g.point.y += y_transform;
936 }
937 line.x_offset = x_offset;
938
939 line_start = line.end;
940 }
941
942 let y_transform_px = Px(y_transform as i32);
943 self.underline -= y_transform_px;
944 self.baseline -= y_transform_px;
945 self.overline -= y_transform_px;
946 self.strikethrough -= y_transform_px;
947 self.underline_descent -= y_transform_px;
948 self.mid_offset = mid_offset.y;
949 }
950
951 let baseline_offset =
953 if self.align.is_baseline() { -self.baseline } else { Px(0) } + if args.align.is_baseline() { self.baseline } else { Px(0) };
954 if baseline_offset != Px(0) {
955 let baseline_offset = baseline_offset.0 as f32;
956 for g in &mut self.glyphs {
957 g.point.y += baseline_offset;
958 }
959 }
960
961 self.align_size = align_size;
962 self.align = args.align;
963 self.direction = args.direction;
964 self.is_inlined = is_inlined;
965
966 self.debug_assert_ranges();
967 }
968 fn reshape_line_height_and_spacing(&mut self, line_height: Px, line_spacing: Px) {
969 let mut update_height = false;
970
971 if self.line_height != line_height {
972 let offset_y = (line_height - self.line_height).0 as f32;
973 let mut offset = 0.0;
974 let center = offset_y / 2.0;
975
976 self.first_line.origin.y += Px(center as i32);
977
978 for (_, r) in self.lines.iter_segs() {
979 let r = self.segments.glyphs_range(r);
980 for g in &mut self.glyphs[r.iter()] {
981 g.point.y += offset + center;
982 }
983
984 offset += offset_y;
985 }
986
987 self.line_height = line_height;
988 update_height = true;
989 }
990
991 if self.line_spacing != line_spacing {
992 if self.lines.is_multi() {
993 let offset_y = (line_spacing - self.line_spacing).0 as f32;
994 let mut offset = offset_y;
995
996 for (_, r) in self.lines.iter_segs_skip(1) {
997 let r = self.segments.glyphs_range(r);
998
999 for g in &mut self.glyphs[r.iter()] {
1000 g.point.y += offset;
1001 }
1002
1003 offset += offset_y;
1004 }
1005 offset -= offset_y;
1006
1007 self.last_line.origin.y += Px(offset as i32);
1008
1009 update_height = true;
1010 }
1011 self.line_spacing = line_spacing;
1012 }
1013
1014 if update_height {
1015 self.update_mid_size();
1016
1017 if !self.is_inlined {
1018 self.update_first_last_lines();
1019 }
1020 }
1021 }
1022
1023 pub fn clear_reshape(&mut self) {
1025 self.reshape_lines(&TextReshapingArgs::default());
1026 }
1027
1028 fn justify_lines_range(&self) -> ops::Range<usize> {
1029 let mut range = 0..self.lines_len();
1030
1031 if !self.is_inlined {
1032 range.end = range.end.saturating_sub(1);
1034 }
1035 range
1038 }
1039
1040 pub fn reshape_lines_justify(&mut self, mode: Justify, lang: &Lang) {
1045 self.clear_justify_impl(true);
1046
1047 if !self.align.is_fill_x() {
1048 return;
1049 }
1050
1051 let mode = mode.resolve(lang);
1052
1053 let range = self.justify_lines_range();
1054
1055 let fill_width = self.align_size.width.0 as f32;
1056 let last_li = range.end.saturating_sub(1);
1057
1058 for li in range.clone() {
1059 let mut count;
1060 let mut space;
1061 let mut line_seg_range;
1062 let mut offset = 0.0;
1063 let mut last_is_space = false;
1064
1065 let mut fill_width = fill_width;
1066 if self.is_inlined {
1067 if li == 0 {
1069 fill_width = self.first_line.width().0 as f32;
1070 } else if li == last_li {
1071 fill_width = self.last_line.width().0 as f32;
1072 }
1073 }
1074
1075 {
1076 let line = self.line(li).unwrap();
1078
1079 count = match mode {
1081 Justify::InterWord => line.segs().filter(|s| s.kind().is_space()).count(),
1082 Justify::InterLetter => line
1083 .segs()
1084 .map(|s| {
1085 if s.kind().is_space() {
1086 s.clusters_count().saturating_sub(1).max(1)
1087 } else if s.kind().is_word() {
1088 s.clusters_count().saturating_sub(1)
1089 } else {
1090 0
1091 }
1092 })
1093 .sum(),
1094 Justify::Auto => unreachable!(),
1095 };
1096
1097 space = fill_width - self.lines.0[li].width;
1099
1100 line_seg_range = 0..line.segs_len();
1101
1102 let mut first_is_space = false;
1104
1105 if let Some(s) = line.seg(0)
1106 && s.kind().is_space()
1107 && (!self.is_inlined || li > 0 || self.first_line.origin.x == Px(0))
1108 {
1109 first_is_space = true;
1111 count -= 1;
1112 space += s.advance();
1113 }
1114 if let Some(s) = line.seg(line.segs_len().saturating_sub(1))
1115 && s.kind().is_space()
1116 && (!self.is_inlined || li < range.end - 1 || about_eq(self.first_line.size.width.0 as f32, fill_width, 1.0))
1117 {
1118 last_is_space = true;
1120 count -= 1;
1121 space += s.advance();
1122 }
1123 if first_is_space {
1124 line_seg_range.start += 1;
1125 let gsi = self.line(li).unwrap().seg_range.start();
1126 let adv = mem::take(&mut self.segments.0[gsi].advance);
1127 offset -= adv;
1128 self.justified.push(adv);
1129 }
1130 if last_is_space {
1131 line_seg_range.end = line_seg_range.end.saturating_sub(1);
1132 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1133 let adv = mem::take(&mut self.segments.0[gsi].advance);
1134 self.justified.push(adv);
1135 }
1136 if line_seg_range.start > line_seg_range.end {
1137 line_seg_range = 0..0;
1138 }
1139 }
1140 let justify_advance = space / count as f32;
1141 self.justified.push(justify_advance);
1142
1143 for si in line_seg_range {
1144 let is_space;
1145 let glyphs_range;
1146 let gsi;
1147 {
1148 let line = self.line(li).unwrap();
1149 let seg = line.seg(si).unwrap();
1150
1151 is_space = seg.kind().is_space();
1152 glyphs_range = seg.glyphs_range();
1153 gsi = line.seg_range.start() + si;
1154 }
1155
1156 let mut cluster = if self.clusters.is_empty() {
1157 0
1158 } else {
1159 self.clusters[glyphs_range.start()]
1160 };
1161 for gi in glyphs_range {
1162 self.glyphs[gi].point.x += offset;
1163
1164 if matches!(mode, Justify::InterLetter) && self.clusters[gi] != cluster {
1165 cluster = self.clusters[gi];
1166 offset += justify_advance;
1167 self.segments.0[gsi].advance += justify_advance;
1168 }
1169 }
1170
1171 let seg = &mut self.segments.0[gsi];
1172 seg.x += offset;
1173 if is_space {
1174 offset += justify_advance;
1175 seg.advance += justify_advance;
1176 }
1177 }
1178 if last_is_space {
1179 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1180 let seg = &mut self.segments.0[gsi];
1181 debug_assert_eq!(seg.advance, 0.0);
1182 seg.x += offset;
1183 }
1184 self.justified.shrink_to_fit();
1185 }
1186
1187 self.justify = mode;
1188 }
1189
1190 pub fn clear_justify(&mut self) {
1194 self.clear_justify_impl(false)
1195 }
1196 fn clear_justify_impl(&mut self, keep_alloc: bool) {
1197 if self.justify_mode().is_none() {
1198 return;
1199 }
1200
1201 let range = self.justify_lines_range();
1202 debug_assert!(range.len() <= self.justified.len());
1203
1204 let mut justified_alloc = mem::take(&mut self.justified);
1205
1206 let mut justified = justified_alloc.drain(..);
1207 for li in range {
1208 let mut line_seg_range;
1209 let mut last_is_space = false;
1210
1211 let mut offset = 0.0;
1212
1213 {
1214 let line = self.line(li).unwrap();
1215
1216 line_seg_range = 0..line.segs_len();
1217
1218 let mut first_is_space = false;
1220
1221 if let Some(s) = line.seg(0) {
1222 first_is_space = s.kind().is_space();
1223 }
1224 if let Some(s) = line.seg(line.segs_len().saturating_sub(1)) {
1225 last_is_space = s.kind().is_space();
1226 }
1227 if first_is_space {
1228 line_seg_range.start += 1;
1229 let gsi = self.line(li).unwrap().seg_range.start();
1230
1231 let adv = justified.next().unwrap();
1232 self.segments.0[gsi].advance = adv;
1233 offset -= adv;
1234 }
1235 if last_is_space {
1236 line_seg_range.end = line_seg_range.end.saturating_sub(1);
1237 let adv = justified.next().unwrap();
1238 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1239 self.segments.0[gsi].advance = adv;
1240 }
1241 if line_seg_range.start > line_seg_range.end {
1242 line_seg_range = 0..0;
1243 }
1244 }
1245
1246 let justify_advance = justified.next().unwrap();
1247
1248 for si in line_seg_range {
1249 let is_space;
1250 let glyphs_range;
1251 let gsi;
1252 {
1253 let line = self.line(li).unwrap();
1254 let seg = line.seg(si).unwrap();
1255
1256 is_space = seg.kind().is_space();
1257 glyphs_range = seg.glyphs_range();
1258 gsi = line.seg_range.start() + si;
1259 }
1260
1261 let mut cluster = if self.clusters.is_empty() {
1262 0
1263 } else {
1264 self.clusters[glyphs_range.start()]
1265 };
1266 for gi in glyphs_range {
1267 self.glyphs[gi].point.x -= offset;
1268
1269 if matches!(self.justify, Justify::InterLetter) && self.clusters[gi] != cluster {
1270 cluster = self.clusters[gi];
1271 offset += justify_advance;
1272 self.segments.0[gsi].advance -= justify_advance;
1273 }
1274 }
1275
1276 let seg = &mut self.segments.0[gsi];
1277 seg.x -= offset;
1278 if is_space {
1279 offset += justify_advance;
1280 seg.advance -= justify_advance;
1281 }
1282 }
1283 if last_is_space {
1284 let gsi = self.line(li).unwrap().seg_range.end().saturating_sub(1);
1285 self.segments.0[gsi].x -= offset;
1286 }
1287 }
1288
1289 self.justify = Justify::Auto;
1290
1291 if keep_alloc {
1292 drop(justified);
1293 self.justified = justified_alloc;
1294 }
1295 }
1296
1297 pub fn paragraph_break(&self) -> ParagraphBreak {
1299 self.paragraph_break
1300 }
1301
1302 pub fn line_height(&self) -> Px {
1304 self.line_height
1305 }
1306
1307 pub fn line_spacing(&self) -> Px {
1309 self.line_spacing
1310 }
1311
1312 pub fn paragraph_spacing(&self) -> Px {
1314 self.paragraph_spacing
1315 }
1316
1317 pub fn baseline(&self) -> Px {
1323 self.baseline
1324 }
1325
1326 pub fn overline(&self) -> Px {
1328 self.overline
1329 }
1330
1331 pub fn strikethrough(&self) -> Px {
1333 self.strikethrough
1334 }
1335
1336 pub fn underline(&self) -> Px {
1338 self.underline
1339 }
1340
1341 pub fn underline_descent(&self) -> Px {
1344 self.underline_descent
1345 }
1346
1347 pub fn is_empty(&self) -> bool {
1349 self.segments.0.is_empty()
1350 }
1351
1352 pub fn lines(&self) -> impl Iterator<Item = ShapedLine<'_>> {
1356 let just_width = self.justify_mode().map(|_| self.align_size.width);
1357 self.lines.iter_segs().enumerate().map(move |(i, (w, r))| ShapedLine {
1358 text: self,
1359 seg_range: r,
1360 index: i,
1361 width: just_width.unwrap_or_else(|| Px(w.round() as i32)),
1362 })
1363 }
1364
1365 pub fn lines_len(&self) -> usize {
1367 self.lines.0.len()
1368 }
1369
1370 pub fn first_wrapped(&self) -> bool {
1372 self.first_wrapped
1373 }
1374
1375 pub fn line(&self, line_idx: usize) -> Option<ShapedLine<'_>> {
1377 if line_idx >= self.lines.0.len() {
1378 None
1379 } else {
1380 self.lines.iter_segs_skip(line_idx).next().map(move |(w, r)| ShapedLine {
1381 text: self,
1382 seg_range: r,
1383 index: line_idx,
1384 width: Px(w.round() as i32),
1385 })
1386 }
1387 }
1388
1389 pub fn empty(&self) -> ShapedText {
1391 ShapedText {
1392 glyphs: vec![],
1393 clusters: vec![],
1394 segments: GlyphSegmentVec(vec![]),
1395 lines: LineRangeVec(vec![LineRange {
1396 end: 0,
1397 width: 0.0,
1398 x_offset: 0.0,
1399 directions: LayoutDirections::empty(),
1400 }]),
1401 fonts: FontRangeVec(vec![FontRange {
1402 font: self.fonts.font(0).clone(),
1403 end: 0,
1404 }]),
1405 images: vec![],
1406 orig_line_height: self.orig_line_height,
1407 orig_line_spacing: self.orig_line_spacing,
1408 orig_first_line: PxSize::zero(),
1409 orig_last_line: PxSize::zero(),
1410 line_height: self.orig_line_height,
1411 line_spacing: self.orig_line_spacing,
1412 paragraph_spacing: self.paragraph_spacing,
1413 paragraph_break: self.paragraph_break,
1414 baseline: self.baseline,
1415 overline: self.overline,
1416 strikethrough: self.strikethrough,
1417 underline: self.underline,
1418 underline_descent: self.underline_descent,
1419 mid_offset: 0.0,
1420 align_size: PxSize::zero(),
1421 align: Align::TOP_LEFT,
1422 justify: Justify::Auto,
1423 justified: vec![],
1424 overflow_align: Align::TOP_LEFT,
1425 direction: LayoutDirection::LTR,
1426 first_wrapped: false,
1427 first_line: PxRect::zero(),
1428 mid_clear: Px(0),
1429 is_inlined: false,
1430 mid_size: PxSize::zero(),
1431 last_line: PxRect::zero(),
1432 has_colored_glyphs: false,
1433 }
1434 }
1435
1436 pub fn can_rewrap(&self, max_width: Px) -> bool {
1440 for line in self.lines() {
1441 if line.width > max_width || line.started_by_wrap() {
1442 return true;
1443 }
1444 }
1445 false
1446 }
1447
1448 fn debug_assert_ranges(&self) {
1449 #[cfg(debug_assertions)]
1450 {
1451 #[allow(unused)]
1452 macro_rules! trace_assert {
1453 ($cond:expr $(,)?) => {
1454 #[allow(clippy::all)]
1455 if !($cond) {
1456 tracing::error!("{}", stringify!($cond));
1457 return;
1458 }
1459 };
1460 ($cond:expr, $($arg:tt)+) => {
1461 #[allow(clippy::all)]
1462 if !($cond) {
1463 tracing::error!($($arg)*);
1464 return;
1465 }
1466 };
1467 }
1468
1469 let mut prev_seg_end = 0;
1470 for seg in &self.segments.0 {
1471 trace_assert!(seg.end >= prev_seg_end);
1472 prev_seg_end = seg.end;
1473 }
1474 trace_assert!(self.segments.0.last().map(|s| s.end == self.glyphs.len()).unwrap_or(true));
1475
1476 let mut prev_line_end = 0;
1477 for (i, line) in self.lines.0.iter().enumerate() {
1478 trace_assert!(line.end >= prev_line_end);
1479 trace_assert!(line.width >= 0.0);
1480
1481 let line_max = line.x_offset + line.width;
1482 let glyphs = self.segments.glyphs_range(IndexRange(prev_line_end, line.end));
1483 for g in &self.glyphs[glyphs.iter()] {
1484 trace_assert!(
1488 g.point.x <= line_max,
1489 "glyph.x({:?}) > line[{i}].x+width({:?})",
1490 g.point.x,
1491 line_max
1492 );
1493 }
1494
1495 let seg_width = self.segments.0[prev_line_end..line.end].iter().map(|s| s.advance).sum::<f32>();
1496 trace_assert!(
1497 seg_width <= line.width,
1498 "seg_width({:?}) > line[{i}].width({:?})",
1499 seg_width,
1500 line.width,
1501 );
1502
1503 prev_line_end = line.end;
1504 }
1505 trace_assert!(self.lines.0.last().map(|l| l.end == self.segments.0.len()).unwrap_or(true));
1506
1507 let mut prev_font_end = 0;
1508 for font in &self.fonts.0 {
1509 trace_assert!(font.end >= prev_font_end);
1510 prev_font_end = font.end;
1511 }
1512 trace_assert!(self.fonts.0.last().map(|f| f.end == self.glyphs.len()).unwrap_or(true));
1513 }
1514 }
1515
1516 pub fn caret_origin(&self, caret: CaretIndex, full_text: &str) -> PxPoint {
1518 let index = caret.index;
1519 let mut end_line = None;
1520 for line in self.line(caret.line).into_iter().chain(self.lines()) {
1521 for seg in line.segs() {
1522 let txt_range = seg.text_range();
1523 if !txt_range.contains(&index) {
1524 continue;
1525 }
1526 let local_index = index - txt_range.start;
1527 let is_rtl = seg.direction().is_rtl();
1528
1529 let seg_rect = seg.rect();
1530 let mut origin = seg_rect.origin;
1531
1532 let clusters = seg.clusters();
1533 let mut cluster_i = 0;
1534 let mut search_lig = true;
1535
1536 if is_rtl {
1537 for (i, c) in clusters.iter().enumerate().rev() {
1538 match (*c as usize).cmp(&local_index) {
1539 cmp::Ordering::Less => {
1540 cluster_i = i;
1541 }
1542 cmp::Ordering::Equal => {
1543 cluster_i = i;
1544 search_lig = false;
1545 break;
1546 }
1547 cmp::Ordering::Greater => break,
1548 }
1549 }
1550 } else {
1551 for (i, c) in clusters.iter().enumerate() {
1552 match (*c as usize).cmp(&local_index) {
1553 cmp::Ordering::Less => {
1554 cluster_i = i;
1555 }
1556 cmp::Ordering::Equal => {
1557 cluster_i = i;
1558 search_lig = false;
1559 break;
1560 }
1561 cmp::Ordering::Greater => break,
1562 }
1563 }
1564 }
1565
1566 let mut origin_x = origin.x.0 as f32;
1567
1568 let mut glyph_take = cluster_i;
1574 if is_rtl {
1575 glyph_take += 1;
1576 }
1577
1578 let mut search_lig_data = None;
1579
1580 'outer: for (font, glyphs) in seg.glyphs_with_x_advance() {
1581 for (g, advance) in glyphs {
1582 search_lig_data = Some((font, g.index, advance));
1583
1584 if glyph_take == 0 {
1585 break 'outer;
1586 }
1587 origin_x += advance;
1588 glyph_take -= 1;
1589 }
1590 }
1591
1592 if search_lig && let Some((font, g_index, advance)) = search_lig_data {
1593 let lig_start = txt_range.start + clusters[cluster_i] as usize;
1594 let lig_end = if is_rtl {
1595 if cluster_i == 0 {
1596 txt_range.end
1597 } else {
1598 txt_range.start + clusters[cluster_i - 1] as usize
1599 }
1600 } else {
1601 clusters
1602 .get(cluster_i + 1)
1603 .map(|c| txt_range.start + *c as usize)
1604 .unwrap_or_else(|| txt_range.end)
1605 };
1606
1607 let maybe_lig = &full_text[lig_start..lig_end];
1608
1609 let lig_len = unicode_segmentation::UnicodeSegmentation::grapheme_indices(maybe_lig, true).count();
1610 if lig_len > 1 {
1611 let lig_taken = &full_text[lig_start..index];
1614 let lig_taken = unicode_segmentation::UnicodeSegmentation::grapheme_indices(lig_taken, true).count();
1615
1616 for (i, lig_advance) in font.ligature_caret_offsets(g_index).enumerate() {
1617 if i == lig_taken {
1618 origin_x += lig_advance;
1620 search_lig = false;
1621 break;
1622 }
1623 }
1624
1625 if search_lig {
1626 let lig_advance = advance * (lig_taken as f32 / lig_len as f32);
1628
1629 if is_rtl {
1630 origin_x -= lig_advance;
1631 } else {
1632 origin_x += lig_advance;
1633 }
1634 }
1635 }
1636 }
1637
1638 origin.x = Px(origin_x.round() as _);
1639 return origin;
1640 }
1641
1642 if line.index == caret.line && line.text_range().end == index && line.ended_by_wrap() {
1643 end_line = Some(line.index);
1645 break;
1646 }
1647 }
1648
1649 let line_end = end_line.unwrap_or_else(|| self.lines_len().saturating_sub(1));
1651 if let Some(line) = self.line(line_end) {
1652 let rect = line.rect();
1653 if self.direction().is_rtl() {
1654 PxPoint::new(rect.min_x(), rect.min_y())
1656 } else {
1657 PxPoint::new(rect.max_x(), rect.min_y())
1659 }
1660 } else {
1661 PxPoint::zero()
1662 }
1663 }
1664
1665 pub fn nearest_line(&self, y: Px) -> Option<ShapedLine<'_>> {
1667 let first_line_max_y = self.first_line.max_y();
1668 if first_line_max_y >= y {
1669 self.line(0)
1670 } else if self.last_line.min_y() <= y {
1671 self.line(self.lines_len().saturating_sub(1))
1672 } else {
1673 let y = y - first_line_max_y;
1674 let line = (y / self.line_height()).0 as usize + 1;
1675 self.lines.iter_segs_skip(line).next().map(move |(w, r)| ShapedLine {
1676 text: self,
1677 seg_range: r,
1678 index: line,
1679 width: Px(w.round() as i32),
1680 })
1681 }
1682 }
1683
1684 pub fn snap_caret_line(&self, mut caret: CaretIndex) -> CaretIndex {
1688 for line in self.lines() {
1689 let range = line.text_range();
1690
1691 if range.start == caret.index {
1692 if line.started_by_wrap() {
1694 if caret.line >= line.index {
1695 caret.line = line.index;
1696 } else {
1697 caret.line = line.index.saturating_sub(1);
1698 }
1699 } else {
1700 caret.line = line.index;
1701 }
1702 return caret;
1703 } else if range.contains(&caret.index) {
1704 caret.line = line.index;
1706 return caret;
1707 }
1708 }
1709 caret.line = self.lines.0.len().saturating_sub(1);
1710 caret
1711 }
1712
1713 pub fn overflow_info(&self, max_size: PxSize, overflow_suffix_width: Px) -> Option<TextOverflowInfo> {
1715 let (last_line, overflow_line) = match self.overflow_line(max_size.height) {
1718 Some(l) => {
1719 if l.index == 0 {
1720 return Some(TextOverflowInfo {
1722 line: 0,
1723 text_char: 0,
1724 included_glyphs: smallvec::smallvec![],
1725 suffix_origin: l.rect().origin.cast().cast_unit(),
1726 });
1727 } else {
1728 (self.line(l.index - 1).unwrap(), l.index)
1729 }
1730 }
1731 None => (self.line(self.lines_len().saturating_sub(1))?, self.lines_len()),
1732 };
1733
1734 let max_width = max_size.width - overflow_suffix_width;
1737
1738 if last_line.width <= max_width {
1739 return if overflow_line < self.lines_len() {
1741 Some(TextOverflowInfo {
1742 line: overflow_line,
1743 text_char: last_line.text_range().end,
1744 included_glyphs: smallvec::smallvec_inline![0..last_line.glyphs_range().end()],
1745 suffix_origin: {
1746 let r = last_line.rect();
1747 let mut o = r.origin;
1748 match self.direction {
1749 LayoutDirection::LTR => o.x += r.width(),
1750 LayoutDirection::RTL => o.x -= overflow_suffix_width,
1751 }
1752 o.cast().cast_unit()
1753 },
1754 })
1755 } else {
1756 None
1757 };
1758 }
1759
1760 let directions = last_line.directions();
1761 if directions == LayoutDirections::BIDI {
1762 let mut included_glyphs = smallvec::SmallVec::<[ops::Range<usize>; 1]>::new_const();
1763
1764 let min_x = match self.direction {
1765 LayoutDirection::LTR => Px(0),
1766 LayoutDirection::RTL => last_line.rect().max_x() - max_width,
1767 };
1768 let max_x = min_x + max_width;
1769
1770 let mut end_seg = None;
1771
1772 for seg in last_line.segs() {
1773 let (x, width) = seg.x_width();
1774 let seg_max_x = x + width;
1775
1776 if x < max_x && seg_max_x >= min_x {
1777 let mut glyphs_range = seg.glyphs_range().iter();
1778 let mut text_range = seg.text_range();
1779 if x < min_x {
1780 if let Some((c, g)) = seg.overflow_char_glyph((width - (min_x - x)).0 as f32) {
1781 glyphs_range.start += g + 1;
1782 text_range.start += c;
1783 }
1784 } else if seg_max_x > max_x
1785 && let Some((c, g)) = seg.overflow_char_glyph((width - seg_max_x - max_x).0 as f32)
1786 {
1787 glyphs_range.end -= g;
1788 text_range.end -= c;
1789 }
1790
1791 if let Some(l) = included_glyphs.last_mut() {
1792 if l.end == glyphs_range.start {
1793 l.end = glyphs_range.end;
1794 } else if glyphs_range.end == l.start {
1795 l.start = glyphs_range.start;
1796 } else {
1797 included_glyphs.push(glyphs_range.clone());
1798 }
1799 } else {
1800 included_glyphs.push(glyphs_range.clone());
1801 }
1802
1803 match self.direction {
1804 LayoutDirection::LTR => {
1805 if let Some((sx, se, gr, tr)) = &mut end_seg {
1806 if x < *sx {
1807 *sx = x;
1808 *se = seg;
1809 *gr = glyphs_range;
1810 *tr = text_range;
1811 }
1812 } else {
1813 end_seg = Some((x, seg, glyphs_range, text_range));
1814 }
1815 }
1816 LayoutDirection::RTL => {
1817 if let Some((smx, se, gr, tr)) = &mut end_seg {
1818 if seg_max_x < *smx {
1819 *smx = seg_max_x;
1820 *se = seg;
1821 *gr = glyphs_range;
1822 *tr = text_range;
1823 }
1824 } else {
1825 end_seg = Some((seg_max_x, seg, glyphs_range, text_range));
1826 }
1827 }
1828 }
1829 }
1830 }
1831
1832 if let Some((_, seg, glyphs_range, text_range)) = end_seg {
1833 Some(match self.direction {
1834 LayoutDirection::LTR => TextOverflowInfo {
1835 line: overflow_line,
1836 text_char: text_range.end,
1837 included_glyphs,
1838 suffix_origin: {
1839 let r = seg.rect();
1840 let seg_range = seg.glyphs_range().iter();
1841 let mut o = r.origin.cast().cast_unit();
1842 let mut w = r.width();
1843 if seg_range != glyphs_range
1844 && let Some(g) = seg.glyph(glyphs_range.end - seg_range.start)
1845 {
1846 o.x = g.1.point.x;
1847 w = Px(0);
1848 }
1849 o.x += w.0 as f32;
1850 o
1851 },
1852 },
1853 LayoutDirection::RTL => TextOverflowInfo {
1854 line: overflow_line,
1855 text_char: text_range.start,
1856 included_glyphs,
1857 suffix_origin: {
1858 let r = seg.rect();
1859 let mut o = r.origin.cast().cast_unit();
1860 let seg_range = seg.glyphs_range().iter();
1861 if seg_range != glyphs_range
1862 && let Some(g) = seg.glyph(glyphs_range.start - seg_range.start)
1863 {
1864 o.x = g.1.point.x;
1865 }
1866 o.x -= overflow_suffix_width.0 as f32;
1867 o
1868 },
1869 },
1870 })
1871 } else {
1872 None
1873 }
1874 } else {
1875 let mut max_width_f32 = max_width.0 as f32;
1877 for seg in last_line.segs() {
1878 let seg_advance = seg.advance();
1879 max_width_f32 -= seg_advance;
1880 if max_width_f32 <= 0.0 {
1881 let seg_text_range = seg.text_range();
1882 let seg_glyphs_range = seg.glyphs_range();
1883
1884 if directions == LayoutDirections::RTL {
1885 let (c, g) = match seg.overflow_char_glyph(seg_advance + max_width_f32) {
1886 Some(r) => r,
1887 None => (seg_text_range.len(), seg_glyphs_range.len()),
1888 };
1889
1890 return Some(TextOverflowInfo {
1891 line: overflow_line,
1892 text_char: seg_text_range.start + c,
1893 included_glyphs: smallvec::smallvec![
1894 0..seg_glyphs_range.start(),
1895 seg_glyphs_range.start() + g + 1..seg_glyphs_range.end()
1896 ],
1897 suffix_origin: {
1898 let mut o = if let Some(g) = seg.glyph(g + 1) {
1899 euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1900 } else {
1901 let rect = seg.rect();
1902 let mut o = rect.origin.cast().cast_unit();
1903 o.x += seg.advance();
1904 o
1905 };
1906 o.x -= overflow_suffix_width.0 as f32;
1907 o
1908 },
1909 });
1910 } else {
1911 let (c, g) = match seg.overflow_char_glyph((max_width - seg.x_width().0).0 as f32) {
1914 Some(r) => r,
1915 None => (seg_text_range.len(), seg_glyphs_range.len()),
1916 };
1917
1918 return Some(TextOverflowInfo {
1919 line: overflow_line,
1920 text_char: seg_text_range.start + c,
1921 included_glyphs: smallvec::smallvec_inline![0..seg_glyphs_range.start() + g],
1922 suffix_origin: {
1923 if let Some(g) = seg.glyph(g) {
1924 euclid::point2(g.1.point.x, seg.rect().origin.y.0 as f32)
1925 } else {
1926 let rect = seg.rect();
1927 let mut o = rect.origin.cast().cast_unit();
1928 o.x += seg.advance();
1929 o
1930 }
1931 },
1932 });
1933 }
1934 }
1935 }
1936 None
1938 }
1939 }
1940
1941 pub fn highlight_rects(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = PxRect> + '_ {
1945 let start_origin = self.caret_origin(range.start, full_txt).x;
1946 let end_origin = self.caret_origin(range.end, full_txt).x;
1947
1948 MergingRectIter::new(
1949 self.lines()
1950 .skip(range.start.line)
1951 .take(range.end.line + 1 - range.start.line)
1952 .flat_map(|l| l.segs())
1953 .skip_while(move |s| s.text_end() <= range.start.index)
1954 .take_while(move |s| s.text_start() < range.end.index)
1955 .map(move |s| {
1956 let mut r = s.rect();
1957
1958 if s.text_start() <= range.start.index {
1959 match s.direction() {
1962 LayoutDirection::LTR => {
1963 r.size.width = r.max_x() - start_origin;
1964 r.origin.x = start_origin;
1965 }
1966 LayoutDirection::RTL => {
1967 r.size.width = start_origin - r.origin.x;
1968 }
1969 }
1970 }
1971 if s.text_end() > range.end.index {
1972 match s.direction() {
1975 LayoutDirection::LTR => {
1976 r.size.width = end_origin - r.origin.x;
1977 }
1978 LayoutDirection::RTL => {
1979 r.size.width = r.max_x() - end_origin;
1980 r.origin.x = end_origin;
1981 }
1982 }
1983 }
1984
1985 r
1986 }),
1987 )
1988 }
1989
1990 pub fn highlight_underlines(&self, range: ops::Range<CaretIndex>, full_txt: &str) -> impl Iterator<Item = (PxPoint, Px)> + '_ {
1996 let offset = self.underline();
1997 self.highlight_rects(range, full_txt).map(move |r| {
1998 let mut origin = r.origin;
1999 origin.y = r.max_y() - offset;
2000 (origin, r.width())
2001 })
2002 }
2003
2004 pub fn clip_lines(
2006 &self,
2007 clip_range: ops::Range<CaretIndex>,
2008 clip_out: bool,
2009 txt: &str,
2010 lines: impl Iterator<Item = (PxPoint, Px)>,
2011 ) -> Vec<(PxPoint, Px)> {
2012 let clips: Vec<_> = self.highlight_rects(clip_range, txt).collect();
2013
2014 let mut out_lines = vec![];
2015
2016 if clip_out {
2017 let mut exclude_buf = vec![];
2018 for (origin, width) in lines {
2019 let line_max = origin.x + width;
2020
2021 for clip in clips.iter() {
2022 if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2023 if origin.x < clip.max_x() && line_max > clip.origin.x {
2025 exclude_buf.push((clip.origin.x, clip.max_x()));
2027 }
2028 }
2029 }
2030
2031 if !exclude_buf.is_empty() {
2032 exclude_buf.sort_by_key(|(s, _)| *s);
2034
2035 if origin.x < exclude_buf[0].0 {
2036 out_lines.push((origin, exclude_buf[0].0 - origin.x));
2038 }
2039 let mut blank_start = exclude_buf[0].1;
2040 for (clip_start, clip_end) in exclude_buf.drain(..).skip(1) {
2041 if clip_start > blank_start {
2042 if line_max > clip_start {
2044 out_lines.push((PxPoint::new(blank_start, origin.y), line_max.min(clip_start) - blank_start));
2046 }
2047 blank_start = clip_end;
2048 }
2049 }
2050 if line_max > blank_start {
2051 out_lines.push((PxPoint::new(blank_start, origin.y), line_max - blank_start));
2053 }
2054 } else {
2055 out_lines.push((origin, width));
2057 }
2058 }
2059 } else {
2060 let mut include_buf = vec![];
2061 for (origin, width) in lines {
2062 let line_max = origin.x + width;
2063
2064 for clip in clips.iter() {
2065 if origin.y >= clip.origin.y && origin.y <= clip.max_y() {
2066 if origin.x < clip.max_x() && line_max > clip.origin.x {
2068 include_buf.push((clip.origin.x, clip.max_x()));
2070 }
2071 }
2072 }
2073
2074 if !include_buf.is_empty() {
2075 include_buf.sort_by_key(|(s, _)| *s);
2076
2077 for (clip_start, clip_end) in include_buf.drain(..) {
2078 let start = clip_start.max(origin.x);
2079 let end = clip_end.min(line_max);
2080
2081 out_lines.push((PxPoint::new(start, origin.y), end - start));
2082 }
2083
2084 include_buf.clear();
2085 }
2086 }
2087 }
2088
2089 out_lines
2090 }
2091}
2092
2093struct ImageGlyphsIter<'a, G>
2094where
2095 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2096{
2097 glyphs: G,
2098 glyphs_i: u32,
2099 images: &'a [(u32, GlyphImage)],
2100 maybe_img: Option<(&'a Font, &'a [GlyphInstance])>,
2101}
2102impl<'a, G> Iterator for ImageGlyphsIter<'a, G>
2103where
2104 G: Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a,
2105{
2106 type Item = (&'a Font, ShapedImageGlyphs<'a>);
2107
2108 fn next(&mut self) -> Option<Self::Item> {
2109 loop {
2110 if let Some((font, glyphs)) = &mut self.maybe_img {
2111 while self.images.first().map(|(i, _)| *i < self.glyphs_i).unwrap_or(false) {
2115 self.images = &self.images[1..];
2116 }
2117
2118 if let Some((i, img)) = self.images.first() {
2119 if *i == self.glyphs_i {
2121 self.glyphs_i += 1;
2123 let mut size = img.0.with(|i| i.size()).cast::<f32>();
2124 let scale = font.size().0 as f32 / size.width.max(size.height);
2125 size *= scale;
2126 let r = (
2127 *font,
2128 ShapedImageGlyphs::Image {
2129 rect: euclid::Rect::new(glyphs[0].point - euclid::vec2(0.0, size.height), size),
2130 base_glyph: glyphs[0].index,
2131 img: &img.0,
2132 },
2133 );
2134 *glyphs = &glyphs[1..];
2135 if glyphs.is_empty() {
2136 self.maybe_img = None;
2137 }
2138 return Some(r);
2139 } else {
2140 let normal = &glyphs[..glyphs.len().min(*i as _)];
2142 self.glyphs_i += normal.len() as u32;
2143
2144 *glyphs = &glyphs[normal.len()..];
2145 let r = (*font, ShapedImageGlyphs::Normal(normal));
2146
2147 if glyphs.is_empty() {
2148 self.maybe_img = None;
2149 }
2150 return Some(r);
2151 }
2152 } else {
2153 let r = (*font, ShapedImageGlyphs::Normal(glyphs));
2155 self.maybe_img = None;
2156 return Some(r);
2157 }
2158 } else if let Some(seq) = self.glyphs.next() {
2159 self.maybe_img = Some(seq);
2161 } else {
2162 return None;
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: &[rustybuzz::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: &[rustybuzz::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_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().ttf()
2820 {
2821 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
2822 let id = ttf_parser::GlyphId(g.index as _);
2823 let ppm = font.size().0 as u16;
2824 let glyphs_i = self.out.glyphs.len() - shaped_seg.glyphs.len() + i;
2825 if let Some(img) = ttf.glyph_raster_image(id, ppm) {
2826 self.push_glyph_raster(glyphs_i as _, img);
2827 } else if cfg!(feature = "svg")
2828 && let Some(img) = ttf.glyph_svg_image(id)
2829 {
2830 self.push_glyph_svg(glyphs_i as _, img);
2831 }
2832 }
2833 }
2834 }
2835
2836 fn push_glyph_raster(&mut self, glyphs_i: u32, img: ttf_parser::RasterGlyphImage) {
2837 use ttf_parser::RasterImageFormat;
2838 let size = PxSize::new(Px(img.width as _), Px(img.height as _));
2839 let bgra_fmt = ImageDataFormat::Bgra8 {
2840 size,
2841 density: None,
2842 original_color_type: ColorType::BGRA8,
2843 };
2844 let bgra_len = img.width as usize * img.height as usize * 4;
2845 let (data, fmt) = match img.format {
2846 RasterImageFormat::PNG => (img.data.to_vec(), ImageDataFormat::from("png")),
2847 RasterImageFormat::BitmapMono => {
2848 let mut bgra = Vec::with_capacity(bgra_len);
2850 let bytes_per_row = (img.width as usize).div_ceil(8);
2851 for y in 0..img.height as usize {
2852 let row_start = y * bytes_per_row;
2853 for x in 0..img.width as usize {
2854 let byte_index = row_start + x / 8;
2855 let bit_index = 7 - (x % 8);
2856 let bit = (img.data[byte_index] >> bit_index) & 1;
2857 let color = if bit == 1 { [0, 0, 0, 255] } else { [255, 255, 255, 255] };
2858 bgra.extend_from_slice(&color);
2859 }
2860 }
2861 (bgra, bgra_fmt)
2862 }
2863 RasterImageFormat::BitmapMonoPacked => {
2864 let mut bgra = Vec::with_capacity(bgra_len);
2866 for &c8 in img.data {
2867 for bit in 0..8 {
2868 let color = if (c8 >> (7 - bit)) & 1 == 1 {
2869 [0, 0, 0, 255]
2870 } else {
2871 [255, 255, 255, 255]
2872 };
2873 bgra.extend_from_slice(&color);
2874 if bgra.len() == bgra_len {
2875 break;
2876 }
2877 }
2878 }
2879 (bgra, bgra_fmt)
2880 }
2881 RasterImageFormat::BitmapGray2 => {
2882 let mut bgra = Vec::with_capacity(bgra_len);
2884 let bytes_per_row = (img.width as usize).div_ceil(4);
2885 for y in 0..img.height as usize {
2886 let row_start = y * bytes_per_row;
2887 for x in 0..img.width as usize {
2888 let byte_index = row_start + x / 4;
2889 let shift = (3 - (x % 4)) * 2;
2890 let gray = (img.data[byte_index] >> shift) & 0b11;
2891 let color = match gray {
2892 0b00 => [0, 0, 0, 255], 0b01 => [85, 85, 85, 255], 0b10 => [170, 170, 170, 255], 0b11 => [255, 255, 255, 255], _ => unreachable!(),
2897 };
2898 bgra.extend_from_slice(&color);
2899 }
2900 }
2901 (bgra, bgra_fmt)
2902 }
2903 RasterImageFormat::BitmapGray2Packed => {
2904 let mut bgra = Vec::with_capacity(bgra_len);
2906 for &c4 in img.data {
2907 for i in 0..4 {
2908 let gray = (c4 >> (7 - i * 2)) & 0b11;
2909 let color = match gray {
2910 0b00 => [0, 0, 0, 255], 0b01 => [85, 85, 85, 255], 0b10 => [170, 170, 170, 255], 0b11 => [255, 255, 255, 255], _ => unreachable!(),
2915 };
2916 bgra.extend_from_slice(&color);
2917 if bgra.len() == bgra_len {
2918 break;
2919 }
2920 }
2921 }
2922 (bgra, bgra_fmt)
2923 }
2924 RasterImageFormat::BitmapGray4 => {
2925 let mut bgra = Vec::with_capacity(bgra_len);
2927 let bytes_per_row = (img.width as usize).div_ceil(2);
2928 for y in 0..img.height as usize {
2929 let row_start = y * bytes_per_row;
2930 for x in 0..img.width as usize {
2931 let byte_index = row_start + x / 2;
2932 let shift = if x % 2 == 0 { 4 } else { 0 };
2933 let gray = (img.data[byte_index] >> shift) & 0b1111;
2934 let g = gray * 17;
2935 bgra.extend_from_slice(&[g, g, g, 255]);
2936 }
2937 }
2938 (bgra, bgra_fmt)
2939 }
2940 RasterImageFormat::BitmapGray4Packed => {
2941 let mut bgra = Vec::with_capacity(bgra_len);
2942 for &c2 in img.data {
2943 for i in 0..2 {
2944 let gray = (c2 >> (7 - i * 4)) & 0b1111;
2945 let g = gray * 17;
2946 bgra.extend_from_slice(&[g, g, g, 255]);
2947 if bgra.len() == bgra_len {
2948 break;
2949 }
2950 }
2951 }
2952 (bgra, bgra_fmt)
2953 }
2954 RasterImageFormat::BitmapGray8 => {
2955 let mut bgra = Vec::with_capacity(bgra_len);
2956 for &c in img.data {
2957 bgra.extend_from_slice(&[c, c, c, 255]);
2958 }
2959 (bgra, bgra_fmt)
2960 }
2961 RasterImageFormat::BitmapPremulBgra32 => {
2962 let mut bgra = img.data.to_vec();
2963 for c in bgra.chunks_exact_mut(4) {
2964 let (b, g, r, a) = (c[0], c[1], c[2], c[3]);
2965 let unp = if a == 255 {
2966 [b, g, r]
2967 } else {
2968 [
2969 (b as u32 * 255 / a as u32) as u8,
2970 (g as u32 * 255 / a as u32) as u8,
2971 (r as u32 * 255 / a as u32) as u8,
2972 ]
2973 };
2974 c.copy_from_slice(&unp);
2975 }
2976 (bgra, bgra_fmt)
2977 }
2978 };
2979 self.push_glyph_img(glyphs_i, ImageSource::from((data, fmt)));
2980 }
2981
2982 fn push_glyph_svg(&mut self, glyphs_i: u32, img: ttf_parser::svg::SvgDocument) {
2983 self.push_glyph_img(glyphs_i, ImageSource::from((img.data, ImageDataFormat::from("svg"))));
2984 }
2985
2986 fn push_glyph_img(&mut self, glyphs_i: u32, source: ImageSource) {
2987 let img = IMAGES.image(source, ImageOptions::cache(), None);
2988
2989 self.out.images.push((glyphs_i, GlyphImage(img)));
2990 }
2991
2992 fn push_last_line(&mut self, text: &SegmentedText) {
2993 let directions = self.finish_current_line_bidi(text);
2994 self.out.lines.0.push(LineRange {
2995 end: self.out.segments.0.len(),
2996 width: self.origin.x,
2997 x_offset: 0.0,
2998 directions,
2999 });
3000
3001 self.out.update_mid_size();
3002 self.out.update_first_last_lines();
3003 self.out.orig_first_line = self.out.first_line.size;
3004 self.out.orig_last_line = self.out.last_line.size;
3005 if self.out.is_inlined && self.out.lines.0.len() > 1 {
3006 self.out.last_line.origin.y += self.out.mid_clear;
3007 }
3008 }
3009
3010 fn push_hyphenate(&mut self, seg: &str, font: &Font, shaped_seg: &ShapedSegmentData, info: TextSegment, text: &SegmentedText) -> bool {
3011 if !matches!(self.hyphens, Hyphens::Auto) {
3012 return false;
3013 }
3014
3015 let split_points = HYPHENATION.hyphenate(&self.lang, seg);
3016 self.push_hyphenate_pt(&split_points, 0, font, shaped_seg, seg, info, text)
3017 }
3018
3019 #[allow(clippy::too_many_arguments)]
3020 fn push_hyphenate_pt(
3021 &mut self,
3022 split_points: &[usize],
3023 split_points_sub: usize,
3024 font: &Font,
3025 shaped_seg: &ShapedSegmentData,
3026 seg: &str,
3027 info: TextSegment,
3028 text: &SegmentedText,
3029 ) -> bool {
3030 if split_points.is_empty() {
3031 return false;
3032 }
3033
3034 let mut end_glyph = 0;
3036 let mut end_point_i = 0;
3037 let max_width = self.actual_max_width();
3038 for (i, point) in split_points.iter().enumerate() {
3039 let mut point = *point - split_points_sub;
3040 let mut width = 0.0;
3041 let mut c = u32::MAX;
3042 let mut gi = 0;
3043 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3045 width = g.point.0;
3046 if g.cluster != c {
3047 if point == 0 {
3049 break;
3050 }
3051 c = g.cluster;
3052 point -= 1;
3053 }
3054 gi = i;
3055 }
3056
3057 if self.origin.x + width + self.hyphen_glyphs.0.x_advance > max_width {
3058 if end_glyph == 0 {
3060 end_glyph = gi + 1;
3062 end_point_i = i + 1;
3063 }
3064 break;
3065 } else {
3066 end_glyph = gi + 1;
3068 end_point_i = i + 1;
3069 }
3070 }
3071
3072 let end_glyph_x = shaped_seg.glyphs[end_glyph].point.0;
3074 let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3075
3076 if glyphs_a.is_empty() || glyphs_b.is_empty() {
3077 debug_assert!(false, "invalid hyphenation split");
3078 return false;
3079 }
3080 let end_cluster = glyphs_b[0].cluster;
3081 let (seg_a, seg_b) = seg.split_at(end_cluster as usize);
3082
3083 self.push_glyphs(
3084 &ShapedSegmentData {
3085 glyphs: glyphs_a.to_vec(),
3086 x_advance: end_glyph_x,
3087 y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3088 },
3089 self.word_spacing,
3090 );
3091 self.push_font(font);
3092
3093 self.push_glyphs(&self.hyphen_glyphs.0.clone(), 0.0);
3094 self.push_font(&self.hyphen_glyphs.1.clone());
3095
3096 self.push_text_seg(seg_a, info);
3097
3098 self.push_line_break(true, text);
3099
3100 let mut shaped_seg_b = ShapedSegmentData {
3102 glyphs: glyphs_b.to_vec(),
3103 x_advance: shaped_seg.x_advance - end_glyph_x,
3104 y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3105 };
3106 for g in &mut shaped_seg_b.glyphs {
3107 g.point.0 -= end_glyph_x;
3108 g.cluster -= seg_a.len() as u32;
3109 }
3110
3111 if shaped_seg_b.x_advance > self.actual_max_width() {
3112 if self.push_hyphenate_pt(
3114 &split_points[end_point_i..],
3115 split_points_sub + seg_a.len(),
3116 font,
3117 &shaped_seg_b,
3118 seg_b,
3119 info,
3120 text,
3121 ) {
3122 return true;
3123 }
3124 }
3125
3126 self.push_glyphs(&shaped_seg_b, self.word_spacing);
3128 self.push_text_seg(seg_b, info);
3129 true
3130 }
3131
3132 fn push_glyphs(&mut self, shaped_seg: &ShapedSegmentData, spacing: f32) {
3133 self.out.glyphs.extend(shaped_seg.glyphs.iter().map(|gi| {
3134 let r = GlyphInstance::new(gi.index, euclid::point2(gi.point.0 + self.origin.x, gi.point.1 + self.origin.y));
3135 self.origin.x += spacing;
3136 r
3137 }));
3138 self.out.clusters.extend(shaped_seg.glyphs.iter().map(|gi| gi.cluster));
3139
3140 self.origin.x += shaped_seg.x_advance;
3141 self.origin.y += shaped_seg.y_advance;
3142 }
3143
3144 fn push_line_break(&mut self, soft: bool, text: &SegmentedText) {
3145 if self.out.glyphs.is_empty() && self.allow_first_wrap && soft {
3146 self.out.first_wrapped = true;
3147 } else {
3148 let directions = self.finish_current_line_bidi(text);
3149
3150 self.out.lines.0.push(LineRange {
3151 end: self.out.segments.0.len(),
3152 width: self.origin.x,
3153 x_offset: 0.0,
3154 directions,
3155 });
3156
3157 if self.out.lines.0.len() == 1 {
3158 self.out.first_line = PxRect::from_size(PxSize::new(Px(self.origin.x as i32), Px(self.line_height as i32)));
3159
3160 if !self.out.first_wrapped {
3161 let mid_clear = (self.mid_clear_min - self.line_height).max(0.0).round();
3162 self.origin.y += mid_clear;
3163 self.out.mid_clear = Px(mid_clear as i32);
3164 self.out.mid_offset = mid_clear;
3165 }
3166 }
3167
3168 self.max_line_x = self.origin.x.max(self.max_line_x);
3169
3170 let is_paragraph_break = match self.out.paragraph_break {
3171 ParagraphBreak::None => false,
3172 ParagraphBreak::Line => !soft,
3173 };
3174
3175 self.origin.x = if self.paragraph_indent.1 != is_paragraph_break {
3176 self.paragraph_indent.0
3178 } else {
3179 0.0
3180 };
3181
3182 self.origin.y += if is_paragraph_break {
3183 self.paragraph_spacing
3184 } else {
3185 self.line_height + self.line_spacing
3186 };
3187 }
3188 }
3189
3190 #[must_use]
3191 fn finish_current_line_bidi(&mut self, text: &SegmentedText) -> LayoutDirections {
3192 if self.line_has_rtl {
3193 let seg_start = if self.out.lines.0.is_empty() {
3194 0
3195 } else {
3196 self.out.lines.last().end
3197 };
3198
3199 if self.line_has_ltr {
3200 let line_segs = seg_start..self.out.segments.0.len();
3203
3204 let mut x = 0.0;
3206 for i in text.reorder_line_to_ltr(line_segs) {
3207 let g_range = self.out.segments.glyphs(i);
3208 if g_range.iter().is_empty() {
3209 continue;
3210 }
3211
3212 let glyphs = &mut self.out.glyphs[g_range.iter()];
3213 let offset = x - self.out.segments.0[i].x;
3214 self.out.segments.0[i].x = x;
3215 for g in glyphs {
3216 g.point.x += offset;
3217 }
3218 x += self.out.segments.0[i].advance;
3219 }
3220 } else {
3221 let line_width = self.origin.x;
3223
3224 let mut x = line_width;
3225 for i in seg_start..self.out.segments.0.len() {
3226 x -= self.out.segments.0[i].advance;
3227
3228 let g_range = self.out.segments.glyphs(i);
3229
3230 let glyphs = &mut self.out.glyphs[g_range.iter()];
3231 let offset = x - self.out.segments.0[i].x;
3232 self.out.segments.0[i].x = x;
3233 for g in glyphs {
3234 g.point.x += offset;
3235 }
3236 }
3237 }
3238 }
3239
3240 let mut d = LayoutDirections::empty();
3241 d.set(LayoutDirections::LTR, self.line_has_ltr);
3242 d.set(LayoutDirections::RTL, self.line_has_rtl);
3243
3244 self.line_has_ltr = false;
3245 self.line_has_rtl = false;
3246
3247 d
3248 }
3249
3250 pub fn push_text_seg(&mut self, seg: &str, info: TextSegment) {
3251 let g_len = if let Some(l) = self.out.segments.0.last() {
3252 self.out.glyphs.len() - l.end
3253 } else {
3254 self.out.glyphs.len()
3255 };
3256 if g_len > 0 {
3257 self.line_has_ltr |= info.level.is_ltr();
3258 self.line_has_rtl |= info.level.is_rtl();
3259 }
3260
3261 self.text_seg_end += seg.len();
3262
3263 let is_first_of_line =
3264 (!self.out.lines.0.is_empty() && self.out.lines.last().end == self.out.segments.0.len()) || self.out.segments.0.is_empty();
3265 let x = if is_first_of_line {
3266 0.0
3267 } else {
3268 self.out.segments.0.last().map(|s| s.x + s.advance).unwrap_or(0.0)
3270 };
3271 self.out.segments.0.push(GlyphSegment {
3272 text: TextSegment {
3273 end: self.text_seg_end,
3274 ..info
3275 },
3276 end: self.out.glyphs.len(),
3277 x,
3278 advance: self.origin.x - x,
3279 });
3280 }
3281
3282 pub fn push_split_seg(&mut self, shaped_seg: &ShapedSegmentData, seg: &str, info: TextSegment, spacing: f32, text: &SegmentedText) {
3283 let mut end_glyph = 0;
3284 let mut end_glyph_x = 0.0;
3285 let max_width = self.actual_max_width();
3286 for (i, g) in shaped_seg.glyphs.iter().enumerate() {
3287 if self.origin.x + g.point.0 > max_width {
3288 break;
3289 }
3290 end_glyph = i;
3291 end_glyph_x = g.point.0;
3292 }
3293
3294 let (glyphs_a, glyphs_b) = shaped_seg.glyphs.split_at(end_glyph);
3295
3296 if glyphs_a.is_empty() || glyphs_b.is_empty() {
3297 self.push_line_break(true, text);
3299 self.push_glyphs(shaped_seg, spacing);
3300 self.push_text_seg(seg, info);
3301 } else {
3302 let (seg_a, seg_b) = seg.split_at(glyphs_b[0].cluster as usize);
3303
3304 let shaped_seg_a = ShapedSegmentData {
3305 glyphs: glyphs_a.to_vec(),
3306 x_advance: end_glyph_x,
3307 y_advance: glyphs_a.iter().map(|g| g.point.1).sum(),
3308 };
3309 self.push_glyphs(&shaped_seg_a, spacing);
3310 self.push_text_seg(seg_a, info);
3311 self.push_line_break(true, text);
3312
3313 let mut shaped_seg_b = ShapedSegmentData {
3314 glyphs: glyphs_b.to_vec(),
3315 x_advance: shaped_seg.x_advance - end_glyph_x,
3316 y_advance: glyphs_b.iter().map(|g| g.point.1).sum(),
3317 };
3318 for g in &mut shaped_seg_b.glyphs {
3319 g.point.0 -= shaped_seg_a.x_advance;
3320 g.cluster -= seg_a.len() as u32;
3321 }
3322
3323 if shaped_seg_b.x_advance <= max_width {
3324 self.push_glyphs(&shaped_seg_b, spacing);
3325 self.push_text_seg(seg_b, info);
3326 } else {
3327 self.push_split_seg(&shaped_seg_b, seg_b, info, spacing, text);
3328 }
3329 }
3330 }
3331
3332 fn push_font(&mut self, font: &Font) {
3333 if let Some(last) = self.out.fonts.0.last_mut() {
3334 if &last.font == font {
3335 last.end = self.out.glyphs.len();
3336 return;
3337 } else if last.end == self.out.glyphs.len() {
3338 return;
3339 }
3340 }
3341 self.out.fonts.0.push(FontRange {
3342 font: font.clone(),
3343 end: self.out.glyphs.len(),
3344 })
3345 }
3346}
3347
3348bitflags! {
3349 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3351 #[serde(transparent)]
3352 pub struct LayoutDirections: u8 {
3353 const LTR = 1;
3355 const RTL = 2;
3357 const BIDI = Self::LTR.bits() | Self::RTL.bits();
3361 }
3362}
3363
3364#[derive(Clone, Copy)]
3366pub struct ShapedLine<'a> {
3367 text: &'a ShapedText,
3368 seg_range: IndexRange,
3370 index: usize,
3371 width: Px,
3372}
3373impl fmt::Debug for ShapedLine<'_> {
3374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3375 f.debug_struct("ShapedLine")
3376 .field("seg_range", &self.seg_range)
3377 .field("index", &self.index)
3378 .field("width", &self.width)
3379 .finish_non_exhaustive()
3380 }
3381}
3382impl<'a> ShapedLine<'a> {
3383 pub fn height(&self) -> Px {
3385 if self.index == 0 {
3386 self.text.first_line.height()
3387 } else if self.index == self.text.lines.0.len() - 1 {
3388 self.text.last_line.height()
3389 } else {
3390 self.text.line_height
3391 }
3392 }
3393
3394 pub fn width(&self) -> Px {
3396 if self.index == 0 {
3397 self.text.first_line.width()
3398 } else if self.index == self.text.lines.0.len() - 1 {
3399 self.text.last_line.width()
3400 } else {
3401 self.width
3402 }
3403 }
3404
3405 pub fn rect(&self) -> PxRect {
3407 if self.index == 0 {
3408 return self.text.first_line;
3409 }
3410 if self.index == self.text.lines.0.len() - 1 {
3411 return self.text.last_line;
3412 }
3413
3414 let size = PxSize::new(self.width, self.text.line_height);
3415 let origin = PxPoint::new(
3416 Px(self.text.lines.0[self.index].x_offset as i32),
3417 self.text.line_height * Px((self.index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear,
3418 );
3419 PxRect::new(origin, size)
3420 }
3421
3422 pub fn original_size(&self) -> PxSize {
3429 if self.index == 0 {
3430 return self.text.orig_first_line;
3431 }
3432 if self.index == self.text.lines.0.len() - 1 {
3433 return self.text.orig_last_line;
3434 }
3435 PxSize::new(self.width, self.text.line_height)
3436 }
3437
3438 pub fn is_paragraph_start(&self) -> bool {
3444 match self.text.paragraph_break {
3445 ParagraphBreak::None => self.index == 0,
3446 ParagraphBreak::Line => !self.started_by_wrap(),
3447 }
3448 }
3449
3450 pub fn is_paragraph_end(&self) -> bool {
3456 match self.text.paragraph_break {
3457 ParagraphBreak::None => self.index == self.text.lines.0.len() - 1,
3458 ParagraphBreak::Line => !self.ended_by_wrap(),
3459 }
3460 }
3461
3462 pub fn overline(&self) -> (PxPoint, Px) {
3464 self.decoration_line(self.text.overline)
3465 }
3466
3467 pub fn strikethrough(&self) -> (PxPoint, Px) {
3469 self.decoration_line(self.text.strikethrough)
3470 }
3471
3472 pub fn underline(&self) -> (PxPoint, Px) {
3478 self.decoration_line(self.text.underline)
3479 }
3480
3481 pub fn underline_descent(&self) -> (PxPoint, Px) {
3487 self.decoration_line(self.text.underline_descent)
3488 }
3489
3490 pub fn underline_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3496 MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline()))
3497 }
3498
3499 pub fn underline_descent_skip_spaces(&self) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3505 MergingLineIter::new(self.segs().filter(|s| s.kind().is_word()).map(|s| s.underline_descent()))
3506 }
3507
3508 pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3514 MergingLineIter::new(self.segs().flat_map(move |s| s.underline_skip_glyphs(thickness)))
3515 }
3516
3517 pub fn underline_skip_glyphs_and_spaces(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + 'a {
3523 MergingLineIter::new(
3524 self.segs()
3525 .filter(|s| s.kind().is_word())
3526 .flat_map(move |s| s.underline_skip_glyphs(thickness)),
3527 )
3528 }
3529
3530 fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
3531 let r = self.rect();
3532 let y = r.max_y() - bottom_up_offset;
3533 (PxPoint::new(r.origin.x, y), self.width)
3534 }
3535
3536 fn segments(&self) -> &'a [GlyphSegment] {
3537 &self.text.segments.0[self.seg_range.iter()]
3538 }
3539
3540 pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> + 'a {
3547 let r = self.glyphs_range();
3548 self.text.glyphs_range(r)
3549 }
3550
3551 pub fn glyphs_with_x_advance(&self) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + 'a)> + 'a {
3553 self.segs().flat_map(|s| s.glyphs_with_x_advance())
3554 }
3555
3556 fn glyphs_range(&self) -> IndexRange {
3557 self.text.segments.glyphs_range(self.seg_range)
3558 }
3559
3560 pub fn segs(&self) -> impl DoubleEndedIterator<Item = ShapedSegment<'a>> + ExactSizeIterator + use<'a> {
3562 let text = self.text;
3563 let line_index = self.index;
3564 self.seg_range.iter().map(move |i| ShapedSegment {
3565 text,
3566 line_index,
3567 index: i,
3568 })
3569 }
3570
3571 pub fn segs_len(&self) -> usize {
3573 self.seg_range.len()
3574 }
3575
3576 pub fn seg(&self, seg_idx: usize) -> Option<ShapedSegment<'_>> {
3580 if self.seg_range.len() > seg_idx {
3581 Some(ShapedSegment {
3582 text: self.text,
3583 line_index: self.index,
3584 index: seg_idx + self.seg_range.start(),
3585 })
3586 } else {
3587 None
3588 }
3589 }
3590
3591 pub fn started_by_wrap(&self) -> bool {
3597 self.index > 0 && {
3598 let prev_line = self.text.lines.segs(self.index - 1);
3599 self.text.segments.0[prev_line.iter()]
3600 .last()
3601 .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3602 .unwrap() }
3604 }
3605
3606 pub fn ended_by_wrap(&self) -> bool {
3612 self.index < self.text.lines.0.len() - 1
3614 && self
3615 .segments()
3616 .last()
3617 .map(|s| !matches!(s.text.kind, TextSegmentKind::LineBreak))
3618 .unwrap() }
3620
3621 pub fn actual_line_start(&self) -> Self {
3625 let mut r = *self;
3626 while r.started_by_wrap() {
3627 r = r.text.line(r.index - 1).unwrap();
3628 }
3629 r
3630 }
3631
3632 pub fn actual_line_end(&self) -> Self {
3636 let mut r = *self;
3637 while r.ended_by_wrap() {
3638 r = r.text.line(r.index + 1).unwrap();
3639 }
3640 r
3641 }
3642
3643 pub fn text_range(&self) -> ops::Range<usize> {
3645 let start = self.seg_range.start();
3646 let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3647 let end = self.seg_range.end();
3648 let end = if end == 0 { 0 } else { self.text.segments.0[end - 1].text.end };
3649
3650 start..end
3651 }
3652
3653 pub fn text_caret_range(&self) -> ops::Range<usize> {
3658 let start = self.seg_range.start();
3659 let start = if start == 0 { 0 } else { self.text.segments.0[start - 1].text.end };
3660 let end = self.seg_range.end();
3661 let end = if end == 0 {
3662 0
3663 } else if self.seg_range.start() == end {
3664 start
3665 } else {
3666 let seg = &self.text.segments.0[end - 1];
3667 if !matches!(seg.text.kind, TextSegmentKind::LineBreak) {
3668 seg.text.end
3669 } else {
3670 if end == 1 { 0 } else { self.text.segments.0[end - 2].text.end }
3672 }
3673 };
3674
3675 start..end
3676 }
3677
3678 pub fn actual_text_range(&self) -> ops::Range<usize> {
3680 let start = self.actual_line_start().text_range().start;
3681 let end = self.actual_line_end().text_range().end;
3682 start..end
3683 }
3684
3685 pub fn actual_text_caret_range(&self) -> ops::Range<usize> {
3687 let start = self.actual_line_start().text_range().start;
3688 let end = self.actual_line_end().text_caret_range().end;
3689 start..end
3690 }
3691
3692 pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
3696 let r = self.text_range();
3697
3698 let start = r.start.min(full_text.len());
3699 let end = r.end.min(full_text.len());
3700
3701 &full_text[start..end]
3702 }
3703
3704 pub fn nearest_seg(&self, x: Px) -> Option<ShapedSegment<'a>> {
3706 let mut min = None;
3707 let mut min_dist = Px::MAX;
3708 for seg in self.segs() {
3709 let (seg_x, width) = seg.x_width();
3710 if x >= seg_x {
3711 let seg_max_x = seg_x + width;
3712 if x < seg_max_x {
3713 return Some(seg);
3714 }
3715 }
3716 let dist = (x - seg_x).abs();
3717 if min_dist > dist {
3718 min = Some(seg);
3719 min_dist = dist;
3720 }
3721 }
3722 min
3723 }
3724
3725 pub fn index(&self) -> usize {
3727 self.index
3728 }
3729
3730 pub fn directions(&self) -> LayoutDirections {
3732 self.text.lines.0[self.index].directions
3733 }
3734}
3735
3736struct MergingLineIter<I> {
3738 iter: I,
3739 line: Option<(PxPoint, Px)>,
3740}
3741impl<I> MergingLineIter<I> {
3742 pub fn new(iter: I) -> Self {
3743 MergingLineIter { iter, line: None }
3744 }
3745}
3746impl<I: Iterator<Item = (PxPoint, Px)>> Iterator for MergingLineIter<I> {
3747 type Item = I::Item;
3748
3749 fn next(&mut self) -> Option<Self::Item> {
3750 loop {
3751 match self.iter.next() {
3752 Some(line) => {
3753 if let Some(prev_line) = &mut self.line {
3754 fn min_x((origin, _width): (PxPoint, Px)) -> Px {
3755 origin.x
3756 }
3757 fn max_x((origin, width): (PxPoint, Px)) -> Px {
3758 origin.x + width
3759 }
3760
3761 if prev_line.0.y == line.0.y && min_x(*prev_line) <= max_x(line) && max_x(*prev_line) >= min_x(line) {
3762 let x = min_x(*prev_line).min(min_x(line));
3763 prev_line.1 = max_x(*prev_line).max(max_x(line)) - x;
3764 prev_line.0.x = x;
3765 } else {
3766 let cut = mem::replace(prev_line, line);
3767 return Some(cut);
3768 }
3769 } else {
3770 self.line = Some(line);
3771 continue;
3772 }
3773 }
3774 None => return self.line.take(),
3775 }
3776 }
3777 }
3778}
3779
3780struct MergingRectIter<I> {
3781 iter: I,
3782 rect: Option<PxBox>,
3783}
3784impl<I> MergingRectIter<I> {
3785 pub fn new(iter: I) -> Self {
3786 MergingRectIter { iter, rect: None }
3787 }
3788}
3789impl<I: Iterator<Item = PxRect>> Iterator for MergingRectIter<I> {
3790 type Item = I::Item;
3791
3792 fn next(&mut self) -> Option<Self::Item> {
3793 loop {
3794 match self.iter.next() {
3795 Some(rect) => {
3796 let rect = rect.to_box2d();
3797 if let Some(prev_rect) = &mut self.rect {
3798 if prev_rect.min.y == rect.min.y
3799 && prev_rect.max.y == rect.max.y
3800 && prev_rect.min.x <= rect.max.x
3801 && prev_rect.max.x >= rect.min.x
3802 {
3803 prev_rect.min.x = prev_rect.min.x.min(rect.min.x);
3804 prev_rect.max.x = prev_rect.max.x.max(rect.max.x);
3805 continue;
3806 } else {
3807 let cut = mem::replace(prev_rect, rect);
3808 return Some(cut.to_rect());
3809 }
3810 } else {
3811 self.rect = Some(rect);
3812 continue;
3813 }
3814 }
3815 None => return self.rect.take().map(|r| r.to_rect()),
3816 }
3817 }
3818 }
3819}
3820
3821#[derive(Clone, Copy)]
3823pub struct ShapedSegment<'a> {
3824 text: &'a ShapedText,
3825 line_index: usize,
3826 index: usize,
3827}
3828impl fmt::Debug for ShapedSegment<'_> {
3829 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3830 f.debug_struct("ShapedSegment")
3831 .field("line_index", &self.line_index)
3832 .field("index", &self.index)
3833 .finish_non_exhaustive()
3834 }
3835}
3836impl<'a> ShapedSegment<'a> {
3837 pub fn kind(&self) -> TextSegmentKind {
3839 self.text.segments.0[self.index].text.kind
3840 }
3841
3842 pub fn level(&self) -> BidiLevel {
3844 self.text.segments.0[self.index].text.level
3845 }
3846
3847 pub fn direction(&self) -> LayoutDirection {
3849 self.text.segments.0[self.index].text.direction()
3850 }
3851
3852 pub fn has_last_glyph(&self) -> bool {
3854 let seg_glyphs = self.text.segments.glyphs(self.index);
3855 let s = self.text.lines.segs(self.line_index);
3856 let line_glyphs = self.text.segments.glyphs_range(s);
3857 seg_glyphs.end() == line_glyphs.end()
3858 }
3859
3860 fn glyphs_range(&self) -> IndexRange {
3861 self.text.segments.glyphs(self.index)
3862 }
3863
3864 pub fn glyphs(&self) -> impl Iterator<Item = (&'a Font, &'a [GlyphInstance])> {
3877 let r = self.glyphs_range();
3878 self.text.glyphs_range(r)
3879 }
3880
3881 pub fn glyph(&self, index: usize) -> Option<(&'a Font, GlyphInstance)> {
3883 let mut r = self.glyphs_range();
3884 r.0 += index;
3885 self.text.glyphs_range(r).next().map(|(f, g)| (f, g[0]))
3886 }
3887
3888 pub fn clusters(&self) -> &[u32] {
3896 let r = self.glyphs_range();
3897 self.text.clusters_range(r)
3898 }
3899
3900 pub fn clusters_count(&self) -> usize {
3904 let mut c = u32::MAX;
3905 let mut count = 0;
3906 for &i in self.clusters() {
3907 if i != c {
3908 c = i;
3909 count += 1;
3910 }
3911 }
3912 count
3913 }
3914
3915 pub fn ligature_segs_count(&self) -> usize {
3918 let range = self.glyphs_range();
3919 if range.iter().is_empty() {
3920 0
3921 } else {
3922 self.text.segments.0[self.index + 1..]
3923 .iter()
3924 .filter(|s| s.end == range.end())
3925 .count()
3926 }
3927 }
3928
3929 pub fn glyphs_with_x_advance(
3933 &self,
3934 ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (GlyphInstance, f32)> + use<'a>)> + use<'a> {
3935 let r = self.glyphs_range();
3936 self.text.seg_glyphs_with_x_advance(self.index, r)
3937 }
3938
3939 pub fn cluster_glyphs_with_x_advance(
3943 &self,
3944 ) -> impl Iterator<Item = (&'a Font, impl Iterator<Item = (u32, &'a [GlyphInstance], f32)> + use<'a>)> + use<'a> {
3945 let r = self.glyphs_range();
3946 self.text.seg_cluster_glyphs_with_x_advance(self.index, r)
3947 }
3948
3949 pub fn x_width(&self) -> (Px, Px) {
3951 let IndexRange(start, end) = self.glyphs_range();
3952
3953 let is_line_break = start == end && matches!(self.kind(), TextSegmentKind::LineBreak);
3954
3955 let start_x = match self.direction() {
3956 LayoutDirection::LTR => {
3957 if is_line_break || start == self.text.glyphs.len() {
3958 let x = self.text.lines.x_offset(self.line_index);
3959 let w = self.text.lines.width(self.line_index);
3960 return (Px((x + w) as i32), Px(0));
3961 }
3962 self.text.glyphs[start].point.x
3963 }
3964 LayoutDirection::RTL => {
3965 if is_line_break || start == self.text.glyphs.len() {
3966 let x = self.text.lines.x_offset(self.line_index);
3967 return (Px(x as i32), Px(0));
3968 }
3969
3970 self.text.glyphs[start..end]
3971 .iter()
3972 .map(|g| g.point.x)
3973 .min_by(f32::total_cmp)
3974 .unwrap_or(0.0)
3975 }
3976 };
3977
3978 (Px(start_x.floor() as i32), Px(self.advance().ceil() as i32))
3979 }
3980
3981 pub fn advance(&self) -> f32 {
3983 self.text.segments.0[self.index].advance
3984 }
3985
3986 pub fn rect(&self) -> PxRect {
3988 let (x, width) = self.x_width();
3989 let size = PxSize::new(width, self.text.line_height);
3990
3991 let y = if self.line_index == 0 {
3992 self.text.first_line.origin.y
3993 } else if self.line_index == self.text.lines.0.len() - 1 {
3994 self.text.last_line.origin.y
3995 } else {
3996 self.text.line_height * Px((self.line_index - 1) as i32) + self.text.first_line.max_y() + self.text.mid_clear
3997 };
3998 PxRect::new(PxPoint::new(x, y), size)
3999 }
4000
4001 pub fn overflow_char_glyph(&self, max_width_px: f32) -> Option<(usize, usize)> {
4003 if self.advance() > max_width_px {
4004 match self.direction() {
4005 LayoutDirection::LTR => {
4006 let mut x = 0.0;
4007 let mut g = 0;
4008 for (_, c) in self.cluster_glyphs_with_x_advance() {
4009 for (cluster, glyphs, advance) in c {
4010 x += advance;
4011 if x > max_width_px {
4012 return Some((cluster as usize, g));
4013 }
4014 g += glyphs.len();
4015 }
4016 }
4017 }
4018 LayoutDirection::RTL => {
4019 let mut g = 0;
4020 let mut rev = smallvec::SmallVec::<[_; 10]>::new();
4021 for (_, c) in self.cluster_glyphs_with_x_advance() {
4022 for (cluster, glyphs, advance) in c {
4023 rev.push((cluster, g, advance));
4024 g += glyphs.len();
4025 }
4026 }
4027
4028 let mut x = 0.0;
4029 for (c, g, advance) in rev.into_iter().rev() {
4030 x += advance;
4031 if x > max_width_px {
4032 return Some((c as usize, g));
4033 }
4034 }
4035 }
4036 }
4037 }
4038 None
4039 }
4040
4041 pub fn inline_info(&self) -> InlineSegmentInfo {
4043 let (x, width) = self.x_width();
4044 InlineSegmentInfo::new(x, width)
4045 }
4046
4047 fn decoration_line(&self, bottom_up_offset: Px) -> (PxPoint, Px) {
4048 let (x, width) = self.x_width();
4049 let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - bottom_up_offset;
4050 (PxPoint::new(x, y), width)
4051 }
4052
4053 pub fn overline(&self) -> (PxPoint, Px) {
4055 self.decoration_line(self.text.overline)
4056 }
4057
4058 pub fn strikethrough(&self) -> (PxPoint, Px) {
4060 self.decoration_line(self.text.strikethrough)
4061 }
4062
4063 pub fn underline(&self) -> (PxPoint, Px) {
4069 self.decoration_line(self.text.underline)
4070 }
4071
4072 pub fn underline_skip_glyphs(&self, thickness: Px) -> impl Iterator<Item = (PxPoint, Px)> + use<'a> {
4076 let y = (self.text.line_height * Px((self.line_index as i32) + 1)) - self.text.underline;
4077 let (x, _) = self.x_width();
4078
4079 let line_y = -(self.text.baseline - self.text.underline).0 as f32;
4080 let line_y_range = (line_y, line_y - thickness.0 as f32);
4081
4082 let padding = (thickness.0 as f32).clamp(1.0, (self.text.fonts.font(0).size().0 as f32 * 0.2).max(1.0));
4084
4085 struct UnderlineSkipGlyphs<'a, I, J> {
4087 line_y_range: (f32, f32),
4088 y: Px,
4089 padding: f32,
4090 min_width: Px,
4091
4092 iter: I,
4093 resume: Option<(&'a Font, J)>,
4094 x: f32,
4095 width: f32,
4096 }
4097 impl<I, J> UnderlineSkipGlyphs<'_, I, J> {
4098 fn line(&self) -> Option<(PxPoint, Px)> {
4099 fn f32_to_px(px: f32) -> Px {
4100 Px(px.round() as i32)
4101 }
4102 let r = (PxPoint::new(f32_to_px(self.x), self.y), f32_to_px(self.width));
4103 if r.1 >= self.min_width { Some(r) } else { None }
4104 }
4105 }
4106 impl<'a, I, J> Iterator for UnderlineSkipGlyphs<'a, I, J>
4107 where
4108 I: Iterator<Item = (&'a Font, J)>,
4109 J: Iterator<Item = (GlyphInstance, f32)>,
4110 {
4111 type Item = (PxPoint, Px);
4112
4113 fn next(&mut self) -> Option<Self::Item> {
4114 loop {
4115 let continuation = self.resume.take().or_else(|| self.iter.next());
4116 if let Some((font, mut glyphs_with_adv)) = continuation {
4117 for (g, a) in &mut glyphs_with_adv {
4118 if let Some((ex_start, ex_end)) = font.h_line_hits(g.index, self.line_y_range) {
4119 self.width += ex_start - self.padding;
4120 let r = self.line();
4121 self.x += self.width + self.padding + ex_end + self.padding;
4122 self.width = a - (ex_start + ex_end) - self.padding;
4123
4124 if r.is_some() {
4125 self.resume = Some((font, glyphs_with_adv));
4126 return r;
4127 }
4128 } else {
4129 self.width += a;
4130 }
4132 }
4133 } else {
4134 let r = self.line();
4135 self.width = 0.0;
4136 return r;
4137 }
4138 }
4139 }
4140 }
4141 UnderlineSkipGlyphs {
4142 line_y_range,
4143 y,
4144 padding,
4145 min_width: Px((padding / 2.0).max(1.0).ceil() as i32),
4146
4147 iter: self.glyphs_with_x_advance(),
4148 resume: None,
4149 x: x.0 as f32,
4150 width: 0.0,
4151 }
4152 }
4153
4154 pub fn underline_descent(&self) -> (PxPoint, Px) {
4160 self.decoration_line(self.text.underline_descent)
4161 }
4162
4163 pub fn text_range(&self) -> ops::Range<usize> {
4165 self.text_start()..self.text_end()
4166 }
4167
4168 pub fn text_start(&self) -> usize {
4170 if self.index == 0 {
4171 0
4172 } else {
4173 self.text.segments.0[self.index - 1].text.end
4174 }
4175 }
4176
4177 pub fn text_end(&self) -> usize {
4179 self.text.segments.0[self.index].text.end
4180 }
4181
4182 pub fn text_glyph_range(&self, glyph_range: impl ops::RangeBounds<usize>) -> ops::Range<usize> {
4186 let included_start = match glyph_range.start_bound() {
4187 ops::Bound::Included(i) => Some(*i),
4188 ops::Bound::Excluded(i) => Some(*i + 1),
4189 ops::Bound::Unbounded => None,
4190 };
4191 let excluded_end = match glyph_range.end_bound() {
4192 ops::Bound::Included(i) => Some(*i - 1),
4193 ops::Bound::Excluded(i) => Some(*i),
4194 ops::Bound::Unbounded => None,
4195 };
4196
4197 let glyph_range_start = self.glyphs_range().start();
4198 let glyph_to_char = |g| self.text.clusters[glyph_range_start + g] as usize;
4199
4200 match (included_start, excluded_end) {
4201 (None, None) => IndexRange(0, self.text_range().len()),
4202 (None, Some(end)) => IndexRange(0, glyph_to_char(end)),
4203 (Some(start), None) => IndexRange(glyph_to_char(start), self.text_range().len()),
4204 (Some(start), Some(end)) => IndexRange(glyph_to_char(start), glyph_to_char(end)),
4205 }
4206 .iter()
4207 }
4208
4209 pub fn text<'s>(&self, full_text: &'s str) -> &'s str {
4213 let r = self.text_range();
4214 let start = r.start.min(full_text.len());
4215 let end = r.end.min(full_text.len());
4216 &full_text[start..end]
4217 }
4218
4219 pub fn nearest_char_index(&self, x: Px, full_text: &str) -> usize {
4221 let txt_range = self.text_range();
4222 let is_rtl = self.direction().is_rtl();
4223 let x = x.0 as f32;
4224
4225 let seg_clusters = self.clusters();
4226
4227 for (font, clusters) in self.cluster_glyphs_with_x_advance() {
4228 for (cluster, glyphs, advance) in clusters {
4229 let found = x < glyphs[0].point.x || glyphs[0].point.x + advance > x;
4230 if !found {
4231 continue;
4232 }
4233 let cluster_i = seg_clusters.iter().position(|&c| c == cluster).unwrap();
4234
4235 let char_a = txt_range.start + cluster as usize;
4236 let char_b = if is_rtl {
4237 if cluster_i == 0 {
4238 txt_range.end
4239 } else {
4240 txt_range.start + seg_clusters[cluster_i - 1] as usize
4241 }
4242 } else {
4243 let next_cluster = cluster_i + glyphs.len();
4244 if next_cluster == seg_clusters.len() {
4245 txt_range.end
4246 } else {
4247 txt_range.start + seg_clusters[next_cluster] as usize
4248 }
4249 };
4250
4251 if char_b - char_a > 1 && glyphs.len() == 1 {
4252 let text = &full_text[char_a..char_b];
4255
4256 let mut lig_parts = smallvec::SmallVec::<[u16; 6]>::new_const();
4257 for (i, _) in unicode_segmentation::UnicodeSegmentation::grapheme_indices(text, true) {
4258 lig_parts.push(i as u16);
4259 }
4260
4261 if lig_parts.len() > 1 {
4262 let x = x - glyphs[0].point.x;
4265
4266 let mut split = true;
4267 for (i, font_caret) in font.ligature_caret_offsets(glyphs[0].index).enumerate() {
4268 if i == lig_parts.len() {
4269 break;
4270 }
4271 split = false;
4272
4273 if font_caret > x {
4274 return char_a + lig_parts[i] as usize;
4276 }
4277 }
4278 if split {
4279 let lig_part = advance / lig_parts.len() as f32;
4281 let mut lig_x = lig_part;
4282 if is_rtl {
4283 for c in lig_parts.into_iter().rev() {
4284 if lig_x > x {
4285 return char_a + c as usize;
4287 }
4288 lig_x += lig_part;
4289 }
4290 } else {
4291 for c in lig_parts {
4292 if lig_x > x {
4293 return char_a + c as usize;
4294 }
4295 lig_x += lig_part;
4296 }
4297 }
4298 }
4299 }
4300 }
4301 let middle_x = glyphs[0].point.x + advance / 2.0;
4304
4305 return if is_rtl {
4306 if x <= middle_x { char_b } else { char_a }
4307 } else if x <= middle_x {
4308 char_a
4309 } else {
4310 char_b
4311 };
4312 }
4313 }
4314
4315 let mut start = is_rtl;
4316 if matches!(self.kind(), TextSegmentKind::LineBreak) {
4317 start = !start;
4318 }
4319 if start { txt_range.start } else { txt_range.end }
4320 }
4321
4322 pub fn index(&self) -> usize {
4324 self.index - self.text.lines.segs(self.line_index).start()
4325 }
4326}
4327
4328const WORD_CACHE_MAX_LEN: usize = 32;
4329const WORD_CACHE_MAX_ENTRIES: usize = 10_000;
4330
4331#[derive(Hash, PartialEq, Eq)]
4332pub(super) struct WordCacheKey<S> {
4333 string: S,
4334 ctx_key: WordContextKey,
4335}
4336#[derive(Hash)]
4337struct WordCacheKeyRef<'a, S> {
4338 string: &'a S,
4339 ctx_key: &'a WordContextKey,
4340}
4341
4342#[derive(Hash, PartialEq, Eq, Clone)]
4343pub(super) struct WordContextKey {
4344 lang: unic_langid::subtags::Language,
4345 script: Option<unic_langid::subtags::Script>,
4346 direction: LayoutDirection,
4347 features: Box<[usize]>,
4348}
4349impl WordContextKey {
4350 pub fn new(lang: &Lang, direction: LayoutDirection, font_features: &RFontFeatures) -> Self {
4351 let is_64 = mem::size_of::<usize>() == mem::size_of::<u64>();
4352
4353 let mut features = vec![];
4354
4355 if !font_features.is_empty() {
4356 features.reserve(font_features.len() * if is_64 { 3 } else { 4 });
4357 for feature in font_features {
4358 if is_64 {
4359 let mut h = feature.tag.0 as u64;
4360 h |= (feature.value as u64) << 32;
4361 features.push(h as usize);
4362 } else {
4363 features.push(feature.tag.0 as usize);
4364 features.push(feature.value as usize);
4365 }
4366
4367 features.push(feature.start as usize);
4368 features.push(feature.end as usize);
4369 }
4370 }
4371
4372 WordContextKey {
4373 lang: lang.language,
4374 script: lang.script,
4375 direction,
4376 features: features.into_boxed_slice(),
4377 }
4378 }
4379
4380 pub fn harfbuzz_lang(&self) -> Option<rustybuzz::Language> {
4381 self.lang.as_str().parse().ok()
4382 }
4383
4384 pub fn harfbuzz_script(&self) -> Option<rustybuzz::Script> {
4385 let t: u32 = self.script?.into();
4386 let t = t.to_le_bytes(); rustybuzz::Script::from_iso15924_tag(ttf_parser::Tag::from_bytes(&[t[0], t[1], t[2], t[3]]))
4388 }
4389
4390 pub fn harfbuzz_direction(&self) -> rustybuzz::Direction {
4391 into_harf_direction(self.direction)
4392 }
4393}
4394
4395#[derive(Debug, Clone, Default)]
4396pub(super) struct ShapedSegmentData {
4397 glyphs: Vec<ShapedGlyph>,
4398 x_advance: f32,
4399 y_advance: f32,
4400}
4401#[derive(Debug, Clone, Copy)]
4402struct ShapedGlyph {
4403 index: u32,
4405 cluster: u32,
4407 point: (f32, f32),
4408}
4409
4410impl Font {
4411 fn buffer_segment(&self, segment: &str, key: &WordContextKey) -> rustybuzz::UnicodeBuffer {
4412 let mut buffer = rustybuzz::UnicodeBuffer::new();
4413 buffer.set_direction(key.harfbuzz_direction());
4414 buffer.set_cluster_level(rustybuzz::BufferClusterLevel::MonotoneCharacters);
4415
4416 if let Some(lang) = key.harfbuzz_lang() {
4417 buffer.set_language(lang);
4418 }
4419 if let Some(script) = key.harfbuzz_script() {
4420 buffer.set_script(script);
4421 }
4422
4423 buffer.push_str(segment);
4424 buffer
4425 }
4426
4427 fn shape_segment_no_cache(&self, seg: &str, key: &WordContextKey, features: &[rustybuzz::Feature]) -> ShapedSegmentData {
4428 let buffer = if let Some(font) = self.harfbuzz() {
4429 let buffer = self.buffer_segment(seg, key);
4430 rustybuzz::shape(&font, features, buffer)
4431 } else {
4432 return ShapedSegmentData {
4433 glyphs: vec![],
4434 x_advance: 0.0,
4435 y_advance: 0.0,
4436 };
4437 };
4438
4439 let size_scale = self.metrics().size_scale;
4440 let to_layout = |p: i32| p as f32 * size_scale;
4441
4442 let mut w_x_advance = 0.0;
4443 let mut w_y_advance = 0.0;
4444
4445 let glyphs: Vec<_> = buffer
4446 .glyph_infos()
4447 .iter()
4448 .zip(buffer.glyph_positions())
4449 .map(|(i, p)| {
4450 let x_offset = to_layout(p.x_offset);
4451 let y_offset = -to_layout(p.y_offset);
4452 let x_advance = to_layout(p.x_advance);
4453 let y_advance = to_layout(p.y_advance);
4454
4455 let point = (w_x_advance + x_offset, w_y_advance + y_offset);
4456 w_x_advance += x_advance;
4457 w_y_advance += y_advance;
4458
4459 ShapedGlyph {
4460 index: i.glyph_id,
4461 cluster: i.cluster,
4462 point,
4463 }
4464 })
4465 .collect();
4466
4467 ShapedSegmentData {
4468 glyphs,
4469 x_advance: w_x_advance,
4470 y_advance: w_y_advance,
4471 }
4472 }
4473
4474 fn shape_segment<R>(
4475 &self,
4476 seg: &str,
4477 word_ctx_key: &WordContextKey,
4478 features: &[rustybuzz::Feature],
4479 out: impl FnOnce(&ShapedSegmentData) -> R,
4480 ) -> R {
4481 if !(1..=WORD_CACHE_MAX_LEN).contains(&seg.len()) || self.face().is_empty() {
4482 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4483 out(&seg)
4484 } else if let Some(small) = Self::to_small_word(seg) {
4485 let cache = self.0.small_word_cache.read();
4487
4488 let hash = cache.hasher().hash_one(WordCacheKeyRef {
4489 string: &small,
4490 ctx_key: word_ctx_key,
4491 });
4492
4493 if let Some((_, seg)) = cache
4494 .raw_entry()
4495 .from_hash(hash, |e| e.string == small && &e.ctx_key == word_ctx_key)
4496 {
4497 return out(seg);
4498 }
4499 drop(cache);
4500
4501 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4503 let key = WordCacheKey {
4504 string: small,
4505 ctx_key: word_ctx_key.clone(),
4506 };
4507 let r = out(&seg);
4508 let mut cache = self.0.small_word_cache.write();
4509 if cache.len() > WORD_CACHE_MAX_ENTRIES {
4510 cache.clear();
4511 }
4512 cache.insert(key, seg);
4513 r
4514 } else {
4515 let cache = self.0.word_cache.read();
4517
4518 let hash = cache.hasher().hash_one(WordCacheKeyRef {
4519 string: &seg,
4520 ctx_key: word_ctx_key,
4521 });
4522
4523 if let Some((_, seg)) = cache
4524 .raw_entry()
4525 .from_hash(hash, |e| e.string.as_str() == seg && &e.ctx_key == word_ctx_key)
4526 {
4527 return out(seg);
4528 }
4529 drop(cache);
4530
4531 let string = seg.to_owned();
4533 let seg = self.shape_segment_no_cache(seg, word_ctx_key, features);
4534 let key = WordCacheKey {
4535 string,
4536 ctx_key: word_ctx_key.clone(),
4537 };
4538 let r = out(&seg);
4539 let mut cache = self.0.word_cache.write();
4540 if cache.len() > WORD_CACHE_MAX_ENTRIES {
4541 cache.clear();
4542 }
4543 cache.insert(key, seg);
4544 r
4545 }
4546 }
4547
4548 pub fn space_index(&self) -> GlyphIndex {
4550 self.shape_space().0
4551 }
4552
4553 pub fn space_x_advance(&self) -> Px {
4555 self.shape_space().1
4556 }
4557
4558 fn shape_space(&self) -> (GlyphIndex, Px) {
4559 let mut id = 0;
4560 let mut adv = 0.0;
4561 self.shape_segment(
4562 " ",
4563 &WordContextKey {
4564 lang: unic_langid::subtags::Language::from_bytes(b"und").unwrap(),
4565 script: None,
4566 direction: LayoutDirection::LTR,
4567 features: Box::new([]),
4568 },
4569 &[],
4570 |r| {
4571 id = r.glyphs.last().map(|g| g.index).unwrap_or(0);
4572 adv = r.x_advance;
4573 },
4574 );
4575 (id, Px(adv as _))
4576 }
4577
4578 pub fn shape_text(self: &Font, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4580 ShapedTextBuilder::shape_text(std::slice::from_ref(self), text, config)
4581 }
4582
4583 pub fn outline(&self, glyph_id: GlyphIndex, sink: &mut impl OutlineSink) -> Option<PxRect> {
4587 struct AdapterSink<'a, S> {
4588 sink: &'a mut S,
4589 scale: f32,
4590 }
4591 impl<S: OutlineSink> ttf_parser::OutlineBuilder for AdapterSink<'_, S> {
4592 fn move_to(&mut self, x: f32, y: f32) {
4593 self.sink.move_to(euclid::point2(x, y) * self.scale)
4594 }
4595
4596 fn line_to(&mut self, x: f32, y: f32) {
4597 self.sink.line_to(euclid::point2(x, y) * self.scale)
4598 }
4599
4600 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
4601 let ctrl = euclid::point2(x1, y1) * self.scale;
4602 let to = euclid::point2(x, y) * self.scale;
4603 self.sink.quadratic_curve_to(ctrl, to)
4604 }
4605
4606 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
4607 let l_from = euclid::point2(x1, y1) * self.scale;
4608 let l_to = euclid::point2(x2, y2) * self.scale;
4609 let to = euclid::point2(x, y) * self.scale;
4610 self.sink.cubic_curve_to((l_from, l_to), to)
4611 }
4612
4613 fn close(&mut self) {
4614 self.sink.close()
4615 }
4616 }
4617
4618 let scale = self.metrics().size_scale;
4619
4620 let f = self.harfbuzz()?;
4621 let r = f.outline_glyph(ttf_parser::GlyphId(glyph_id as _), &mut AdapterSink { sink, scale })?;
4622 Some(
4623 PxRect::new(
4624 PxPoint::new(Px(r.x_min as _), Px(r.y_min as _)),
4625 PxSize::new(Px(r.width() as _), Px(r.height() as _)),
4626 ) * Factor2d::uniform(scale),
4627 )
4628 }
4629
4630 pub fn h_line_hits(&self, glyph_id: GlyphIndex, line_y_range: (f32, f32)) -> Option<(f32, f32)> {
4640 struct InterceptsSink {
4646 start: Option<euclid::Point2D<f32, Px>>,
4647 current: euclid::Point2D<f32, Px>,
4648 under: (bool, bool),
4649
4650 line_y_range: (f32, f32),
4651 hit: Option<(f32, f32)>,
4652 }
4653 impl OutlineSink for InterceptsSink {
4654 fn move_to(&mut self, to: euclid::Point2D<f32, Px>) {
4655 self.start = Some(to);
4656 self.current = to;
4657 self.under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4658 }
4659
4660 fn line_to(&mut self, to: euclid::Point2D<f32, Px>) {
4661 let under = (to.y < self.line_y_range.0, to.y < self.line_y_range.1);
4662
4663 if self.under != under || under == (true, false) {
4664 self.under = under;
4666
4667 let (x0, x1) = if self.current.x < to.x {
4668 (self.current.x, to.x)
4669 } else {
4670 (to.x, self.current.x)
4671 };
4672 if let Some((min, max)) = &mut self.hit {
4673 *min = min.min(x0);
4674 *max = max.max(x1);
4675 } else {
4676 self.hit = Some((x0, x1));
4677 }
4678 }
4679
4680 self.current = to;
4681 self.under = under;
4682 }
4683
4684 fn quadratic_curve_to(&mut self, _: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>) {
4685 self.line_to(to);
4686 }
4687
4688 fn cubic_curve_to(&mut self, _: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>) {
4689 self.line_to(to);
4690 }
4691
4692 fn close(&mut self) {
4693 if let Some(s) = self.start.take()
4694 && s != self.current
4695 {
4696 self.line_to(s);
4697 }
4698 }
4699 }
4700 let mut sink = InterceptsSink {
4701 start: None,
4702 current: euclid::point2(0.0, 0.0),
4703 under: (false, false),
4704
4705 line_y_range,
4706 hit: None,
4707 };
4708 self.outline(glyph_id, &mut sink)?;
4709
4710 sink.hit.map(|(a, b)| (a, b - a))
4711 }
4712}
4713
4714pub trait OutlineSink {
4718 fn move_to(&mut self, to: euclid::Point2D<f32, Px>);
4720 fn line_to(&mut self, to: euclid::Point2D<f32, Px>);
4722 fn quadratic_curve_to(&mut self, ctrl: euclid::Point2D<f32, Px>, to: euclid::Point2D<f32, Px>);
4724 fn cubic_curve_to(&mut self, ctrl: (euclid::Point2D<f32, Px>, euclid::Point2D<f32, Px>), to: euclid::Point2D<f32, Px>);
4728 fn close(&mut self);
4730}
4731
4732impl FontList {
4733 pub fn shape_text(&self, text: &SegmentedText, config: &TextShapingArgs) -> ShapedText {
4735 ShapedTextBuilder::shape_text(self, text, config)
4736 }
4737}
4738
4739#[derive(Clone, Copy)]
4741struct IndexRange(pub usize, pub usize);
4742impl fmt::Debug for IndexRange {
4743 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4744 write!(f, "{}..{}", self.0, self.1)
4745 }
4746}
4747impl IntoIterator for IndexRange {
4748 type Item = usize;
4749
4750 type IntoIter = std::ops::Range<usize>;
4751
4752 fn into_iter(self) -> Self::IntoIter {
4753 self.iter()
4754 }
4755}
4756impl From<IndexRange> for std::ops::Range<usize> {
4757 fn from(c: IndexRange) -> Self {
4758 c.iter()
4759 }
4760}
4761impl From<std::ops::Range<usize>> for IndexRange {
4762 fn from(r: std::ops::Range<usize>) -> Self {
4763 IndexRange(r.start, r.end)
4764 }
4765}
4766impl IndexRange {
4767 pub fn from_bounds(bounds: impl ops::RangeBounds<usize>) -> Self {
4768 let start = match bounds.start_bound() {
4770 ops::Bound::Included(&i) => i,
4771 ops::Bound::Excluded(&i) => i + 1,
4772 ops::Bound::Unbounded => 0,
4773 };
4774 let end = match bounds.end_bound() {
4775 ops::Bound::Included(&i) => i + 1,
4776 ops::Bound::Excluded(&i) => i,
4777 ops::Bound::Unbounded => 0,
4778 };
4779 Self(start, end)
4780 }
4781
4782 pub fn iter(self) -> std::ops::Range<usize> {
4784 self.0..self.1
4785 }
4786
4787 pub fn start(self) -> usize {
4789 self.0
4790 }
4791
4792 pub fn end(self) -> usize {
4794 self.1
4795 }
4796
4797 pub fn len(self) -> usize {
4799 self.end() - self.start()
4800 }
4801}
4802impl std::ops::RangeBounds<usize> for IndexRange {
4803 fn start_bound(&self) -> std::ops::Bound<&usize> {
4804 std::ops::Bound::Included(&self.0)
4805 }
4806
4807 fn end_bound(&self) -> std::ops::Bound<&usize> {
4808 std::ops::Bound::Excluded(&self.1)
4809 }
4810}
4811
4812pub fn f32_cmp(a: &f32, b: &f32) -> std::cmp::Ordering {
4814 a.partial_cmp(b).unwrap()
4815}
4816
4817fn into_harf_direction(d: LayoutDirection) -> rustybuzz::Direction {
4818 match d {
4819 LayoutDirection::LTR => rustybuzz::Direction::LeftToRight,
4820 LayoutDirection::RTL => rustybuzz::Direction::RightToLeft,
4821 }
4822}
4823
4824#[cfg(test)]
4825mod tests {
4826 use crate::{
4827 FONTS, Font, FontName, FontStretch, FontStyle, FontWeight, ParagraphBreak, SegmentedText, TextReshapingArgs, TextShapingArgs,
4828 WordContextKey,
4829 };
4830 use zng_app::APP;
4831 use zng_ext_l10n::lang;
4832 use zng_layout::{
4833 context::LayoutDirection,
4834 unit::{Px, PxConstraints2d},
4835 };
4836
4837 fn test_font() -> Font {
4838 let mut app = APP.minimal().run_headless(false);
4839 let font = app
4840 .run_test(async {
4841 FONTS
4842 .normal(&FontName::sans_serif(), &lang!(und))
4843 .wait_rsp()
4844 .await
4845 .unwrap()
4846 .sized(Px(20), vec![])
4847 })
4848 .unwrap();
4849 drop(app);
4850 font
4851 }
4852
4853 #[test]
4854 fn set_line_spacing() {
4855 let text = "0\n1\n2\n3\n4";
4856 test_line_spacing(text, Px(20), Px(0));
4857 test_line_spacing(text, Px(0), Px(20));
4858 test_line_spacing(text, Px(4), Px(6));
4859 test_line_spacing(text, Px(4), Px(4));
4860 test_line_spacing("a line\nanother\nand another", Px(20), Px(0));
4861 test_line_spacing("", Px(20), Px(0));
4862 test_line_spacing("a line", Px(20), Px(0));
4863 }
4864 fn test_line_spacing(text: &'static str, from: Px, to: Px) {
4865 let font = test_font();
4866 let mut config = TextShapingArgs {
4867 line_height: Px(40),
4868 line_spacing: from,
4869 ..Default::default()
4870 };
4871
4872 let text = SegmentedText::new(text, LayoutDirection::LTR);
4873 let mut test = font.shape_text(&text, &config);
4874
4875 config.line_spacing = to;
4876 let expected = font.shape_text(&text, &config);
4877
4878 assert_eq!(from, test.line_spacing());
4879 test.reshape_lines(&TextReshapingArgs {
4880 constraints: PxConstraints2d::new_fill_size(test.align_size()),
4881 inline_constraints: None,
4882 align: test.align(),
4883 overflow_align: test.overflow_align(),
4884 direction: test.direction(),
4885 line_height: test.line_height(),
4886 line_spacing: to,
4887 paragraph_spacing: Px(0),
4888 paragraph_indent: (Px(0), false),
4889 paragraph_break: ParagraphBreak::None,
4890 });
4891 assert_eq!(to, test.line_spacing());
4892
4893 for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4894 assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4895 }
4896
4897 assert_eq!(test.size(), expected.size());
4898 }
4899
4900 #[test]
4901 fn set_line_height() {
4902 let text = "0\n1\n2\n3\n4";
4903 test_line_height(text, Px(20), Px(20));
4904 test_line_height(text, Px(20), Px(10));
4905 test_line_height(text, Px(10), Px(20));
4906 test_line_height("a line\nanother\nand another", Px(20), Px(10));
4907 test_line_height("", Px(20), Px(10));
4908 test_line_height("a line", Px(20), Px(10));
4909 }
4910 fn test_line_height(text: &'static str, from: Px, to: Px) {
4911 let font = test_font();
4912 let mut config = TextShapingArgs {
4913 line_height: from,
4914 line_spacing: Px(20),
4915 ..Default::default()
4916 };
4917
4918 let text = SegmentedText::new(text, LayoutDirection::LTR);
4919 let mut test = font.shape_text(&text, &config);
4920
4921 config.line_height = to;
4922 let expected = font.shape_text(&text, &config);
4923
4924 assert_eq!(from, test.line_height());
4925 test.reshape_lines(&TextReshapingArgs {
4926 constraints: PxConstraints2d::new_fill_size(test.align_size()),
4927 inline_constraints: None,
4928 align: test.align(),
4929 overflow_align: test.overflow_align(),
4930 direction: test.direction(),
4931 line_height: to,
4932 line_spacing: test.line_spacing(),
4933 paragraph_spacing: Px(0),
4934 paragraph_indent: (Px(0), false),
4935 paragraph_break: ParagraphBreak::None,
4936 });
4937 assert_eq!(to, test.line_height());
4938
4939 for (i, (g0, g1)) in test.glyphs.iter().zip(expected.glyphs.iter()).enumerate() {
4940 assert_eq!(g0, g1, "testing {from} to {to}, glyph {i} is not equal");
4941 }
4942
4943 assert_eq!(test.size(), expected.size());
4944 }
4945
4946 #[test]
4947 fn font_fallback_issue() {
4948 let mut app = APP.minimal().run_headless(false);
4949 app.run_test(async {
4950 let font = FONTS
4951 .list(
4952 &[FontName::new("Consolas"), FontName::monospace()],
4953 FontStyle::Normal,
4954 FontWeight::NORMAL,
4955 FontStretch::NORMAL,
4956 &lang!(und),
4957 )
4958 .wait_rsp()
4959 .await
4960 .sized(Px(20), vec![]);
4961
4962 let config = TextShapingArgs::default();
4963
4964 let txt_seg = SegmentedText::new("النص ثنائي الاتجاه (بالإنجليزية:Bi", LayoutDirection::RTL);
4965 let txt_shape = font.shape_text(&txt_seg, &config);
4966
4967 let _ok = (txt_seg, txt_shape);
4968 })
4969 .unwrap()
4970 }
4971
4972 #[test]
4973 fn cluster_is_byte() {
4974 let font = test_font();
4975
4976 let data = font.shape_segment_no_cache("£a", &WordContextKey::new(&lang!("en-US"), LayoutDirection::LTR, &vec![]), &[]);
4977
4978 for ((i, _), g) in "£a".char_indices().zip(&data.glyphs) {
4979 assert_eq!(i as u32, g.cluster);
4980 }
4981 }
4982}