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