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