1#![allow(clippy::too_many_arguments)]
4
5use crate::fallback::FontFallbackIter;
6use crate::{
7 math, Align, Attrs, AttrsList, CacheKeyFlags, Color, DecorationMetrics, DecorationSpan,
8 Ellipsize, EllipsizeHeightLimit, Family, Font, FontSystem, GlyphDecorationData, HashSet,
9 Hinting, LayoutGlyph, LayoutLine, Metrics, Wrap,
10};
11#[cfg(not(feature = "std"))]
12use alloc::{format, vec, vec::Vec};
13
14use alloc::collections::VecDeque;
15use core::cmp::{max, min};
16use core::fmt;
17use core::mem;
18use core::ops::Range;
19
20#[cfg(not(feature = "std"))]
21use core_maths::CoreFloat;
22use fontdb::Style;
23use unicode_script::{Script, UnicodeScript};
24use unicode_segmentation::UnicodeSegmentation;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum Shaping {
29 #[cfg(feature = "swash")]
37 Basic,
38 Advanced,
44}
45
46impl Shaping {
47 fn run(
48 self,
49 glyphs: &mut Vec<ShapeGlyph>,
50 font_system: &mut FontSystem,
51 line: &str,
52 attrs_list: &AttrsList,
53 start_run: usize,
54 end_run: usize,
55 span_rtl: bool,
56 ) {
57 match self {
58 #[cfg(feature = "swash")]
59 Self::Basic => shape_skip(font_system, glyphs, line, attrs_list, start_run, end_run),
60 #[cfg(not(feature = "shape-run-cache"))]
61 Self::Advanced => shape_run(
62 glyphs,
63 font_system,
64 line,
65 attrs_list,
66 start_run,
67 end_run,
68 span_rtl,
69 ),
70 #[cfg(feature = "shape-run-cache")]
71 Self::Advanced => shape_run_cached(
72 glyphs,
73 font_system,
74 line,
75 attrs_list,
76 start_run,
77 end_run,
78 span_rtl,
79 ),
80 }
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
86pub enum Direction {
87 #[default]
89 Auto,
90 LeftToRight,
92 RightToLeft,
94}
95
96impl Direction {
97 fn bidi_level(self) -> Option<unicode_bidi::Level> {
100 match self {
101 Self::Auto => None,
102 Self::LeftToRight => Some(unicode_bidi::Level::ltr()),
103 Self::RightToLeft => Some(unicode_bidi::Level::rtl()),
104 }
105 }
106}
107
108const NUM_SHAPE_PLANS: usize = 6;
109
110#[derive(Default)]
112pub struct ShapeBuffer {
113 shape_plan_cache: VecDeque<(fontdb::ID, harfrust::ShapePlan)>,
116
117 harfrust_buffer: Option<harfrust::UnicodeBuffer>,
119
120 scripts: Vec<Script>,
122
123 spans: Vec<ShapeSpan>,
125
126 words: Vec<ShapeWord>,
128
129 span_decoration_data: Vec<(Range<usize>, GlyphDecorationData)>,
133
134 span_decoration_offsets: Vec<Range<usize>>,
136
137 visual_lines: Vec<VisualLine>,
139 cached_visual_lines: Vec<VisualLine>,
140
141 glyph_sets: Vec<Vec<LayoutGlyph>>,
143}
144
145impl fmt::Debug for ShapeBuffer {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.pad("ShapeBuffer { .. }")
148 }
149}
150
151fn shape_fallback(
152 scratch: &mut ShapeBuffer,
153 glyphs: &mut Vec<ShapeGlyph>,
154 font: &Font,
155 line: &str,
156 attrs_list: &AttrsList,
157 start_run: usize,
158 end_run: usize,
159 span_rtl: bool,
160) -> HashSet<usize> {
161 let run = &line[start_run..end_run];
162
163 let font_scale = font.metrics().units_per_em as f32;
164 let ascent = font.metrics().ascent / font_scale;
165 let descent = -font.metrics().descent / font_scale;
166
167 let mut buffer = scratch.harfrust_buffer.take().unwrap_or_default();
168 buffer.set_direction(if span_rtl {
169 harfrust::Direction::RightToLeft
170 } else {
171 harfrust::Direction::LeftToRight
172 });
173 if run.contains('\t') {
174 buffer.push_str(&run.replace('\t', " "));
179 } else {
180 buffer.push_str(run);
181 }
182 buffer.guess_segment_properties();
183
184 let rtl = matches!(buffer.direction(), harfrust::Direction::RightToLeft);
185 assert_eq!(rtl, span_rtl);
186
187 let attrs = attrs_list.get_span(start_run);
188 let mut rb_font_features = Vec::new();
189
190 for feature in attrs.font_features.iter() {
192 rb_font_features.push(harfrust::Feature::new(
193 harfrust::Tag::new(feature.tag.as_bytes()),
194 feature.value,
195 0..usize::MAX,
196 ));
197 }
198
199 let language = buffer.language();
200 let key = harfrust::ShapePlanKey::new(Some(buffer.script()), buffer.direction())
201 .features(&rb_font_features)
202 .instance(Some(font.shaper_instance()))
203 .language(language.as_ref());
204
205 let shape_plan = match scratch
206 .shape_plan_cache
207 .iter()
208 .find(|(id, plan)| *id == font.id() && key.matches(plan))
209 {
210 Some((_font_id, plan)) => plan,
211 None => {
212 let plan = harfrust::ShapePlan::new(
213 font.shaper(),
214 buffer.direction(),
215 Some(buffer.script()),
216 buffer.language().as_ref(),
217 &rb_font_features,
218 );
219 if scratch.shape_plan_cache.len() >= NUM_SHAPE_PLANS {
220 scratch.shape_plan_cache.pop_front();
221 }
222 scratch.shape_plan_cache.push_back((font.id(), plan));
223 &scratch
224 .shape_plan_cache
225 .back()
226 .expect("we just pushed the shape plan")
227 .1
228 }
229 };
230
231 let glyph_buffer = font.shaper().shape(
232 buffer,
233 harfrust::ShapeOptions::new()
234 .plan(Some(shape_plan))
235 .features(&rb_font_features),
236 );
237 let glyph_infos = glyph_buffer.glyph_infos();
238 let glyph_positions = glyph_buffer.glyph_positions();
239
240 let mut missing = HashSet::default();
241 glyphs.reserve(glyph_infos.len());
242 let glyph_start = glyphs.len();
243 for (info, pos) in glyph_infos.iter().zip(glyph_positions.iter()) {
244 let start_glyph = start_run + info.cluster as usize;
245
246 if info.glyph_id == 0 {
247 missing.insert(start_glyph);
248 }
249
250 let attrs = attrs_list.get_span(start_glyph);
251 let x_advance = pos.x_advance as f32 / font_scale
252 + attrs.letter_spacing_opt.map_or(0.0, |spacing| spacing.0);
253 let y_advance = pos.y_advance as f32 / font_scale;
254 let x_offset = pos.x_offset as f32 / font_scale;
255 let y_offset = pos.y_offset as f32 / font_scale;
256
257 glyphs.push(ShapeGlyph {
258 start: start_glyph,
259 end: end_run, x_advance,
261 y_advance,
262 x_offset,
263 y_offset,
264 ascent,
265 descent,
266 font_monospace_em_width: font.monospace_em_width(),
267 font_id: font.id(),
268 font_weight: attrs.weight,
269 glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"),
270 color_opt: attrs.color_opt,
272 metadata: attrs.metadata,
273 cache_key_flags: override_fake_italic(attrs.cache_key_flags, font, &attrs),
274 metrics_opt: attrs.metrics_opt.map(Into::into),
275 });
276 }
277
278 if rtl {
280 for i in glyph_start + 1..glyphs.len() {
281 let next_start = glyphs[i - 1].start;
282 let next_end = glyphs[i - 1].end;
283 let prev = &mut glyphs[i];
284 if prev.start == next_start {
285 prev.end = next_end;
286 } else {
287 prev.end = next_start;
288 }
289 }
290 } else {
291 for i in (glyph_start + 1..glyphs.len()).rev() {
292 let next_start = glyphs[i].start;
293 let next_end = glyphs[i].end;
294 let prev = &mut glyphs[i - 1];
295 if prev.start == next_start {
296 prev.end = next_end;
297 } else {
298 prev.end = next_start;
299 }
300 }
301 }
302
303 scratch.harfrust_buffer = Some(glyph_buffer.clear());
305
306 missing
307}
308
309fn shape_run(
310 glyphs: &mut Vec<ShapeGlyph>,
311 font_system: &mut FontSystem,
312 line: &str,
313 attrs_list: &AttrsList,
314 start_run: usize,
315 end_run: usize,
316 span_rtl: bool,
317) {
318 let mut scripts = {
320 let mut scripts = mem::take(&mut font_system.shape_buffer.scripts);
321 scripts.clear();
322 scripts
323 };
324 for c in line[start_run..end_run].chars() {
325 match c.script() {
326 Script::Common | Script::Inherited | Script::Latin | Script::Unknown => (),
327 script => {
328 if !scripts.contains(&script) {
329 scripts.push(script);
330 }
331 }
332 }
333 }
334
335 log::trace!(" Run {:?}: '{}'", &scripts, &line[start_run..end_run],);
336
337 let attrs = attrs_list.get_span(start_run);
338
339 let fonts = font_system.get_font_matches(&attrs);
340
341 let default_families = [&attrs.family];
342 let mut font_iter = FontFallbackIter::new(
343 font_system,
344 &fonts,
345 &default_families,
346 &scripts,
347 &line[start_run..end_run],
348 attrs.weight,
349 );
350
351 let font = font_iter.next().expect("no default font found");
352
353 let glyph_start = glyphs.len();
354 let mut missing = {
355 let scratch = font_iter.shape_caches();
356 shape_fallback(
357 scratch, glyphs, &font, line, attrs_list, start_run, end_run, span_rtl,
358 )
359 };
360
361 while !missing.is_empty() {
363 let Some(font) = font_iter.next() else {
364 break;
365 };
366
367 log::trace!(
368 "Evaluating fallback with font '{}'",
369 font_iter.face_name(font.id())
370 );
371 let mut fb_glyphs = Vec::new();
372 let scratch = font_iter.shape_caches();
373 let fb_missing = shape_fallback(
374 scratch,
375 &mut fb_glyphs,
376 &font,
377 line,
378 attrs_list,
379 start_run,
380 end_run,
381 span_rtl,
382 );
383
384 fb_glyphs
386 .retain(|glyph| missing.contains(&glyph.start) && !fb_missing.contains(&glyph.start));
387
388 let mut added = HashSet::default();
390 for fb_glyph in &fb_glyphs {
391 let start = fb_glyph.start;
392 let end = fb_glyph.end;
393
394 for i in start..end {
395 missing.remove(&i);
396 added.insert(i);
397 }
398 }
399
400 if added.is_empty() {
401 continue;
402 }
403
404 glyphs.retain(|glyph| !added.contains(&glyph.start));
406
407 glyphs.extend(fb_glyphs);
409 if span_rtl {
410 (&mut glyphs[glyph_start..]).sort_by(|a, b| b.start.cmp(&a.start));
411 } else {
412 (&mut glyphs[glyph_start..]).sort_by(|a, b| a.start.cmp(&b.start));
413 }
414 }
415
416 font_iter.check_missing(&line[start_run..end_run]);
418
419 font_system.shape_buffer.scripts = scripts;
427}
428
429#[cfg(feature = "shape-run-cache")]
430fn shape_run_cached(
431 glyphs: &mut Vec<ShapeGlyph>,
432 font_system: &mut FontSystem,
433 line: &str,
434 attrs_list: &AttrsList,
435 start_run: usize,
436 end_run: usize,
437 span_rtl: bool,
438) {
439 use crate::{AttrsOwned, ShapeRunKey};
440
441 let run_range = start_run..end_run;
442 let mut key = ShapeRunKey {
443 text: line[run_range.clone()].to_string(),
444 default_attrs: AttrsOwned::new(&attrs_list.defaults()),
445 attrs_spans: Vec::new(),
446 };
447 for (attrs_range, attrs) in attrs_list.spans.overlapping(&run_range) {
448 if attrs == &key.default_attrs {
449 continue;
451 }
452 let start = max(attrs_range.start, start_run).saturating_sub(start_run);
453 let end = min(attrs_range.end, end_run).saturating_sub(start_run);
454 if end > start {
455 let range = start..end;
456 key.attrs_spans.push((range, attrs.clone()));
457 }
458 }
459 if let Some(cache_glyphs) = font_system.shape_run_cache.get(&key) {
460 for mut glyph in cache_glyphs.iter().cloned() {
461 glyph.start += start_run;
463 glyph.end += start_run;
464 glyphs.push(glyph);
465 }
466 return;
467 }
468
469 let mut cache_glyphs = Vec::new();
471 shape_run(
472 &mut cache_glyphs,
473 font_system,
474 line,
475 attrs_list,
476 start_run,
477 end_run,
478 span_rtl,
479 );
480 glyphs.extend_from_slice(&cache_glyphs);
481 for glyph in cache_glyphs.iter_mut() {
482 glyph.start -= start_run;
484 glyph.end -= start_run;
485 }
486 font_system.shape_run_cache.insert(key, cache_glyphs);
487}
488
489#[cfg(feature = "swash")]
490fn shape_skip(
491 font_system: &mut FontSystem,
492 glyphs: &mut Vec<ShapeGlyph>,
493 line: &str,
494 attrs_list: &AttrsList,
495 start_run: usize,
496 end_run: usize,
497) {
498 let attrs = attrs_list.get_span(start_run);
499 let fonts = font_system.get_font_matches(&attrs);
500
501 let default_families = [&attrs.family];
502 let mut font_iter = FontFallbackIter::new(
503 font_system,
504 &fonts,
505 &default_families,
506 &[],
507 "",
508 attrs.weight,
509 );
510
511 let font = font_iter.next().expect("no default font found");
512 let glyph_start = glyphs.len();
513
514 shape_skip_glyphs(glyphs, &font, line, attrs_list, start_run, end_run);
515
516 if matches!(attrs.family, Family::Name(_))
519 && glyphs[glyph_start..].iter().any(|g| g.glyph_id == 0)
520 {
521 let is_mono = font_system
522 .db()
523 .face(font.id())
524 .is_some_and(|face| face.monospaced);
525 let fb_family = if is_mono {
526 Family::Monospace
527 } else {
528 Family::SansSerif
529 };
530 let fb_attrs = Attrs::new()
531 .family(fb_family)
532 .weight(attrs.weight)
533 .style(attrs.style)
534 .stretch(attrs.stretch);
535 let fb_fonts = font_system.get_font_matches(&fb_attrs);
536 let fb_families = [&fb_family];
537 let mut fb_iter =
538 FontFallbackIter::new(font_system, &fb_fonts, &fb_families, &[], "", attrs.weight);
539
540 if let Some(fb_font) = fb_iter.next() {
541 let fb_swash = fb_font.as_swash();
542 let fb_charmap = fb_swash.charmap();
543 let fb_metrics = fb_swash.metrics(&[]);
544 let fb_glyph_metrics = fb_swash.glyph_metrics(&[]).scale(1.0);
545 let fb_scale = f32::from(fb_metrics.units_per_em);
546
547 for glyph in glyphs[glyph_start..].iter_mut() {
548 if glyph.glyph_id != 0 {
549 continue;
550 }
551 let codepoint = line[glyph.start..glyph.end].chars().next().unwrap_or('\0');
552 let glyph_id = fb_charmap.map(codepoint);
553 if glyph_id != 0 {
554 let span_attrs = attrs_list.get_span(glyph.start);
555 glyph.glyph_id = glyph_id;
556 glyph.font_id = fb_font.id();
557 glyph.font_monospace_em_width = fb_font.monospace_em_width();
558 glyph.ascent = fb_metrics.ascent / fb_scale;
559 glyph.descent = fb_metrics.descent / fb_scale;
560 glyph.x_advance = fb_glyph_metrics.advance_width(glyph_id)
561 + span_attrs
562 .letter_spacing_opt
563 .map_or(0.0, |spacing| spacing.0);
564 glyph.cache_key_flags = override_fake_italic(
565 span_attrs.cache_key_flags,
566 fb_font.as_ref(),
567 &span_attrs,
568 );
569 }
570 }
571 }
572 }
573}
574
575#[cfg(feature = "swash")]
576fn shape_skip_glyphs(
577 glyphs: &mut Vec<ShapeGlyph>,
578 font: &Font,
579 line: &str,
580 attrs_list: &AttrsList,
581 start_run: usize,
582 end_run: usize,
583) {
584 let font_id = font.id();
585 let font_monospace_em_width = font.monospace_em_width();
586 let swash_font = font.as_swash();
587
588 let charmap = swash_font.charmap();
589 let metrics = swash_font.metrics(&[]);
590 let glyph_metrics = swash_font.glyph_metrics(&[]).scale(1.0);
591
592 let ascent = metrics.ascent / f32::from(metrics.units_per_em);
593 let descent = metrics.descent / f32::from(metrics.units_per_em);
594
595 glyphs.extend(
596 line[start_run..end_run]
597 .char_indices()
598 .map(|(chr_idx, codepoint)| {
599 let glyph_id = charmap.map(codepoint);
600 let x_advance = glyph_metrics.advance_width(glyph_id)
601 + attrs_list
602 .get_span(start_run + chr_idx)
603 .letter_spacing_opt
604 .map_or(0.0, |spacing| spacing.0);
605 let attrs = attrs_list.get_span(start_run + chr_idx);
606
607 ShapeGlyph {
608 start: chr_idx + start_run,
609 end: chr_idx + start_run + codepoint.len_utf8(),
610 x_advance,
611 y_advance: 0.0,
612 x_offset: 0.0,
613 y_offset: 0.0,
614 ascent,
615 descent,
616 font_monospace_em_width,
617 font_id,
618 font_weight: attrs.weight,
619 glyph_id,
620 color_opt: attrs.color_opt,
621 metadata: attrs.metadata,
622 cache_key_flags: override_fake_italic(attrs.cache_key_flags, font, &attrs),
623 metrics_opt: attrs.metrics_opt.map(Into::into),
624 }
625 }),
626 );
627}
628
629fn override_fake_italic(
630 cache_key_flags: CacheKeyFlags,
631 font: &Font,
632 attrs: &Attrs,
633) -> CacheKeyFlags {
634 if !font.italic_or_oblique && (attrs.style == Style::Italic || attrs.style == Style::Oblique) {
635 cache_key_flags | CacheKeyFlags::FAKE_ITALIC
636 } else {
637 cache_key_flags
638 }
639}
640
641#[derive(Clone, Debug)]
643pub struct ShapeGlyph {
644 pub start: usize,
645 pub end: usize,
646 pub x_advance: f32,
647 pub y_advance: f32,
648 pub x_offset: f32,
649 pub y_offset: f32,
650 pub ascent: f32,
651 pub descent: f32,
652 pub font_monospace_em_width: Option<f32>,
653 pub font_id: fontdb::ID,
654 pub font_weight: fontdb::Weight,
655 pub glyph_id: u16,
656 pub color_opt: Option<Color>,
657 pub metadata: usize,
658 pub cache_key_flags: CacheKeyFlags,
659 pub metrics_opt: Option<Metrics>,
660}
661
662impl ShapeGlyph {
663 const fn layout(
664 &self,
665 font_size: f32,
666 line_height_opt: Option<f32>,
667 x: f32,
668 y: f32,
669 w: f32,
670 level: unicode_bidi::Level,
671 ) -> LayoutGlyph {
672 LayoutGlyph {
673 start: self.start,
674 end: self.end,
675 font_size,
676 line_height_opt,
677 font_id: self.font_id,
678 font_weight: self.font_weight,
679 glyph_id: self.glyph_id,
680 x,
681 y,
682 w,
683 ascender: font_size * self.ascent,
684 descender: font_size * self.descent,
685 level,
686 x_offset: self.x_offset,
687 y_offset: self.y_offset,
688 color_opt: self.color_opt,
689 metadata: self.metadata,
690 cache_key_flags: self.cache_key_flags,
691 }
692 }
693
694 pub fn width(&self, font_size: f32) -> f32 {
697 self.metrics_opt.map_or(font_size, |x| x.font_size) * self.x_advance
698 }
699}
700
701fn decoration_metrics(font: &Font) -> (DecorationMetrics, DecorationMetrics, f32) {
702 let metrics = font.metrics();
703 let upem = metrics.units_per_em as f32;
704 if upem == 0.0 {
705 return (
706 DecorationMetrics::default(),
707 DecorationMetrics::default(),
708 0.0,
709 );
710 }
711 (
712 DecorationMetrics {
713 offset: metrics.underline.map_or(-0.125, |d| d.offset / upem),
714 thickness: metrics.underline.map_or(1.0 / 14.0, |d| d.thickness / upem),
715 },
716 DecorationMetrics {
717 offset: metrics.strikeout.map_or(0.3, |d| d.offset / upem),
718 thickness: metrics.strikeout.map_or(1.0 / 14.0, |d| d.thickness / upem),
719 },
720 metrics.ascent / upem,
721 )
722}
723
724const ELLIPSIS_SPAN: usize = usize::MAX;
726
727fn shape_ellipsis(
728 font_system: &mut FontSystem,
729 attrs: &Attrs,
730 shaping: Shaping,
731 span_rtl: bool,
732) -> Vec<ShapeGlyph> {
733 let attrs_list = AttrsList::new(attrs);
734 let level = if span_rtl {
735 unicode_bidi::Level::rtl()
736 } else {
737 unicode_bidi::Level::ltr()
738 };
739 let word = ShapeWord::new(
740 font_system,
741 "\u{2026}", &attrs_list,
743 0.."\u{2026}".len(),
744 level,
745 false,
746 shaping,
747 );
748 let mut glyphs = word.glyphs;
749
750 if glyphs.is_empty() || glyphs.iter().all(|g| g.glyph_id == 0) {
752 let fallback = ShapeWord::new(
753 font_system,
754 "...",
755 &attrs_list,
756 0.."...".len(),
757 level,
758 false,
759 shaping,
760 );
761 glyphs = fallback.glyphs;
762 }
763 glyphs
764}
765
766#[derive(Clone, Debug)]
768pub struct ShapeWord {
769 pub blank: bool,
770 pub glyphs: Vec<ShapeGlyph>,
771}
772
773impl ShapeWord {
774 pub(crate) fn empty() -> Self {
778 Self {
779 blank: true,
780 glyphs: Vec::default(),
781 }
782 }
783
784 #[allow(clippy::too_many_arguments)]
786 pub fn new(
787 font_system: &mut FontSystem,
788 line: &str,
789 attrs_list: &AttrsList,
790 word_range: Range<usize>,
791 level: unicode_bidi::Level,
792 blank: bool,
793 shaping: Shaping,
794 ) -> Self {
795 let mut empty = Self::empty();
796 empty.build(
797 font_system,
798 line,
799 attrs_list,
800 word_range,
801 level,
802 blank,
803 shaping,
804 );
805 empty
806 }
807
808 #[allow(clippy::too_many_arguments)]
812 pub fn build(
813 &mut self,
814 font_system: &mut FontSystem,
815 line: &str,
816 attrs_list: &AttrsList,
817 word_range: Range<usize>,
818 level: unicode_bidi::Level,
819 blank: bool,
820 shaping: Shaping,
821 ) {
822 let word = &line[word_range.clone()];
823
824 log::trace!(
825 " Word{}: '{}'",
826 if blank { " BLANK" } else { "" },
827 word
828 );
829
830 let mut glyphs = mem::take(&mut self.glyphs);
831 glyphs.clear();
832
833 let span_rtl = level.is_rtl();
834
835 let is_simple_ascii =
837 word.is_ascii() && !word.chars().any(|c| c.is_ascii_control() && c != '\t');
838
839 if is_simple_ascii && !word.is_empty() && {
840 let attrs_start = attrs_list.get_span(word_range.start);
841 attrs_list.spans_iter().all(|(other_range, other_attrs)| {
846 word_range.end <= other_range.start
847 || other_range.end <= word_range.start
848 || attrs_start.compatible(&other_attrs.as_attrs())
849 }) && (attrs_list.spans.gaps(&word_range).next().is_none()
850 || attrs_start.compatible(&attrs_list.defaults()))
851 } {
852 shaping.run(
853 &mut glyphs,
854 font_system,
855 line,
856 attrs_list,
857 word_range.start,
858 word_range.end,
859 span_rtl,
860 );
861 } else {
862 let mut start_run = word_range.start;
864 let mut attrs = attrs_list.defaults();
865 for (egc_i, _egc) in word.grapheme_indices(true) {
866 let start_egc = word_range.start + egc_i;
867 let attrs_egc = attrs_list.get_span(start_egc);
868 if !attrs.compatible(&attrs_egc) {
869 shaping.run(
870 &mut glyphs,
871 font_system,
872 line,
873 attrs_list,
874 start_run,
875 start_egc,
876 span_rtl,
877 );
878
879 start_run = start_egc;
880 attrs = attrs_egc;
881 }
882 }
883 if start_run < word_range.end {
884 shaping.run(
885 &mut glyphs,
886 font_system,
887 line,
888 attrs_list,
889 start_run,
890 word_range.end,
891 span_rtl,
892 );
893 }
894 }
895
896 self.blank = blank;
897 self.glyphs = glyphs;
898 }
899
900 pub fn width(&self, font_size: f32) -> f32 {
902 let mut width = 0.0;
903 for glyph in &self.glyphs {
904 width += glyph.width(font_size);
905 }
906 width
907 }
908}
909
910#[derive(Clone, Debug)]
912pub struct ShapeSpan {
913 pub level: unicode_bidi::Level,
914 pub words: Vec<ShapeWord>,
915 pub byte_range: Range<usize>,
916}
917
918impl ShapeSpan {
919 pub(crate) fn empty() -> Self {
923 Self {
924 level: unicode_bidi::Level::ltr(),
925 words: Vec::default(),
926 byte_range: 0..0,
927 }
928 }
929
930 pub fn new(
932 font_system: &mut FontSystem,
933 line: &str,
934 attrs_list: &AttrsList,
935 span_range: Range<usize>,
936 line_rtl: bool,
937 level: unicode_bidi::Level,
938 shaping: Shaping,
939 ) -> Self {
940 let mut empty = Self::empty();
941 empty.build(
942 font_system,
943 line,
944 attrs_list,
945 span_range,
946 line_rtl,
947 level,
948 shaping,
949 );
950 empty
951 }
952
953 pub fn build(
957 &mut self,
958 font_system: &mut FontSystem,
959 line: &str,
960 attrs_list: &AttrsList,
961 span_range: Range<usize>,
962 line_rtl: bool,
963 level: unicode_bidi::Level,
964 shaping: Shaping,
965 ) {
966 let span = &line[span_range.start..span_range.end];
967
968 log::trace!(
969 " Span {}: '{}'",
970 if level.is_rtl() { "RTL" } else { "LTR" },
971 span
972 );
973
974 let mut words = mem::take(&mut self.words);
975
976 let mut cached_words = mem::take(&mut font_system.shape_buffer.words);
978 cached_words.clear();
979 if line_rtl != level.is_rtl() {
980 cached_words.append(&mut words);
982 } else {
983 cached_words.extend(words.drain(..).rev());
984 }
985
986 let mut start_word = 0;
987 for (end_lb, _) in unicode_linebreak::linebreaks(span) {
988 if end_lb > 0 && end_lb < span.len() {
990 let start_idx = span_range.start;
991 let pre_char = span[..end_lb].chars().last();
992 let post_char = span[end_lb..].chars().next();
993
994 if let (Some(c1), Some(c2)) = (pre_char, post_char) {
995 if c1.is_ascii_punctuation() && c2.is_ascii_punctuation() {
997 let probe_text = format!("{}{}", c1, c2);
998 let attrs = attrs_list.get_span(start_idx + end_lb);
999 let fonts = font_system.get_font_matches(&attrs);
1000 let default_families = [&attrs.family];
1001
1002 let mut font_iter = FontFallbackIter::new(
1003 font_system,
1004 &fonts,
1005 &default_families,
1006 &[],
1007 &probe_text,
1008 attrs.weight,
1009 );
1010
1011 if let Some(font) = font_iter.next() {
1012 let mut glyphs = Vec::new();
1013 let scratch = font_iter.shape_caches();
1014 shape_fallback(
1015 scratch,
1016 &mut glyphs,
1017 &font,
1018 &probe_text,
1019 attrs_list,
1020 0,
1021 probe_text.len(),
1022 false,
1023 );
1024
1025 if glyphs.len() < probe_text.chars().count() {
1027 continue;
1028 }
1029
1030 #[cfg(feature = "swash")]
1034 if glyphs.len() == probe_text.chars().count() {
1035 let charmap = font.as_swash().charmap();
1036 let mut is_modified = false;
1037 for (i, c) in probe_text.chars().enumerate() {
1038 let std_id = charmap.map(c);
1039 if glyphs[i].glyph_id != std_id {
1040 is_modified = true;
1041 break;
1042 }
1043 }
1044
1045 if is_modified {
1046 continue;
1048 }
1049 }
1050 }
1051 }
1052 }
1053 }
1054
1055 let mut start_lb = end_lb;
1056 for (i, c) in span[start_word..end_lb].char_indices().rev() {
1057 if c.is_whitespace() {
1062 start_lb = start_word + i;
1063 } else {
1064 break;
1065 }
1066 }
1067 if start_word < start_lb {
1068 let mut word = cached_words.pop().unwrap_or_else(ShapeWord::empty);
1069 word.build(
1070 font_system,
1071 line,
1072 attrs_list,
1073 (span_range.start + start_word)..(span_range.start + start_lb),
1074 level,
1075 false,
1076 shaping,
1077 );
1078 words.push(word);
1079 }
1080 if start_lb < end_lb {
1081 for (i, c) in span[start_lb..end_lb].char_indices() {
1082 let mut word = cached_words.pop().unwrap_or_else(ShapeWord::empty);
1084 word.build(
1085 font_system,
1086 line,
1087 attrs_list,
1088 (span_range.start + start_lb + i)
1089 ..(span_range.start + start_lb + i + c.len_utf8()),
1090 level,
1091 true,
1092 shaping,
1093 );
1094 words.push(word);
1095 }
1096 }
1097 start_word = end_lb;
1098 }
1099
1100 if line_rtl {
1102 for word in &mut words {
1103 word.glyphs.reverse();
1104 }
1105 }
1106
1107 if line_rtl != level.is_rtl() {
1109 words.reverse();
1110 }
1111
1112 self.level = level;
1113 self.words = words;
1114 self.byte_range = span_range;
1115
1116 font_system.shape_buffer.words = cached_words;
1118 }
1119
1120 pub(crate) fn decorations_into(
1126 &self,
1127 attrs_list: &AttrsList,
1128 font_system: &mut FontSystem,
1129 ) -> Range<usize> {
1130 let span_range = self.byte_range.clone();
1131
1132 let any_decoration = attrs_list.defaults().text_decoration.has_decoration()
1135 || attrs_list.spans_iter().any(|(range, attr_owned)| {
1136 let start = range.start.max(span_range.start);
1137 let end = range.end.min(span_range.end);
1138 start < end && attr_owned.as_attrs().text_decoration.has_decoration()
1139 });
1140 if !any_decoration {
1141 let len = font_system.shape_buffer.span_decoration_data.len();
1142 return len..len;
1143 }
1144
1145 let primary_metrics = self
1147 .words
1148 .iter()
1149 .flat_map(|w| w.glyphs.first())
1150 .next()
1151 .and_then(|glyph| {
1152 font_system
1153 .get_font(glyph.font_id, glyph.font_weight)
1154 .map(|font| decoration_metrics(&font))
1155 });
1156
1157 let out = &mut font_system.shape_buffer.span_decoration_data;
1158 let start = out.len();
1159
1160 if let Some((ul_metrics, st_metrics, ascent)) = primary_metrics {
1161 let mut covered_end = span_range.start;
1163
1164 for (range, attr_owned) in attrs_list.spans_iter() {
1165 let start = range.start.max(span_range.start);
1167 let end = range.end.min(span_range.end);
1168 if start >= end {
1169 continue;
1170 }
1171
1172 if covered_end < start {
1174 let default_attrs = attrs_list.defaults();
1175 if default_attrs.text_decoration.has_decoration() {
1176 out.push((
1177 covered_end..start,
1178 GlyphDecorationData {
1179 text_decoration: default_attrs.text_decoration,
1180 underline_metrics: ul_metrics,
1181 strikethrough_metrics: st_metrics,
1182 ascent,
1183 },
1184 ));
1185 }
1186 }
1187 covered_end = end;
1188
1189 let attrs = attr_owned.as_attrs();
1190 if attrs.text_decoration.has_decoration() {
1191 out.push((
1192 start..end,
1193 GlyphDecorationData {
1194 text_decoration: attrs.text_decoration,
1195 underline_metrics: ul_metrics,
1196 strikethrough_metrics: st_metrics,
1197 ascent,
1198 },
1199 ));
1200 }
1201 }
1202
1203 if covered_end < span_range.end {
1205 let default_attrs = attrs_list.defaults();
1206 if default_attrs.text_decoration.has_decoration() {
1207 out.push((
1208 covered_end..span_range.end,
1209 GlyphDecorationData {
1210 text_decoration: default_attrs.text_decoration,
1211 underline_metrics: ul_metrics,
1212 strikethrough_metrics: st_metrics,
1213 ascent,
1214 },
1215 ));
1216 }
1217 }
1218 }
1219
1220 start..out.len()
1221 }
1222}
1223
1224#[derive(Clone, Debug)]
1226pub struct ShapeLine {
1227 pub rtl: bool,
1228 pub spans: Vec<ShapeSpan>,
1229 pub metrics_opt: Option<Metrics>,
1230 ellipsis_span: Option<ShapeSpan>,
1231}
1232
1233#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
1234struct WordGlyphPos {
1235 word: usize,
1236 glyph: usize,
1237}
1238
1239impl WordGlyphPos {
1240 const ZERO: Self = Self { word: 0, glyph: 0 };
1241 fn new(word: usize, glyph: usize) -> Self {
1242 Self { word, glyph }
1243 }
1244}
1245
1246#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
1247struct SpanWordGlyphPos {
1248 span: usize,
1249 word: usize,
1250 glyph: usize,
1251}
1252
1253impl SpanWordGlyphPos {
1254 const ZERO: Self = Self {
1255 span: 0,
1256 word: 0,
1257 glyph: 0,
1258 };
1259 fn word_glyph_pos(&self) -> WordGlyphPos {
1260 WordGlyphPos {
1261 word: self.word,
1262 glyph: self.glyph,
1263 }
1264 }
1265 fn with_wordglyph(span: usize, wordglyph: WordGlyphPos) -> Self {
1266 Self {
1267 span,
1268 word: wordglyph.word,
1269 glyph: wordglyph.glyph,
1270 }
1271 }
1272}
1273
1274#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1277enum LayoutDirection {
1278 Forward,
1279 Backward,
1280}
1281
1282#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1284struct VlRange {
1285 span: usize,
1286 start: WordGlyphPos,
1287 end: WordGlyphPos,
1288 level: unicode_bidi::Level,
1289}
1290
1291impl Default for VlRange {
1292 fn default() -> Self {
1293 Self {
1294 span: Default::default(),
1295 start: Default::default(),
1296 end: Default::default(),
1297 level: unicode_bidi::Level::ltr(),
1298 }
1299 }
1300}
1301
1302#[derive(Default, Debug)]
1303struct VisualLine {
1304 ranges: Vec<VlRange>,
1305 spaces: u32,
1306 w: f32,
1307 ellipsized: bool,
1308 elided_byte_range: Option<(usize, usize)>,
1311}
1312
1313impl VisualLine {
1314 fn clear(&mut self) {
1315 self.ranges.clear();
1316 self.spaces = 0;
1317 self.w = 0.;
1318 self.ellipsized = false;
1319 self.elided_byte_range = None;
1320 }
1321}
1322
1323impl ShapeLine {
1324 pub(crate) fn empty() -> Self {
1328 Self {
1329 rtl: false,
1330 spans: Vec::default(),
1331 metrics_opt: None,
1332 ellipsis_span: None,
1333 }
1334 }
1335
1336 pub fn new(
1342 font_system: &mut FontSystem,
1343 line: &str,
1344 attrs_list: &AttrsList,
1345 shaping: Shaping,
1346 tab_width: u16,
1347 direction: Direction,
1348 ) -> Self {
1349 let mut empty = Self::empty();
1350 empty.build(font_system, line, attrs_list, shaping, tab_width, direction);
1351 empty
1352 }
1353
1354 pub fn build(
1358 &mut self,
1359 font_system: &mut FontSystem,
1360 line: &str,
1361 attrs_list: &AttrsList,
1362 shaping: Shaping,
1363 tab_width: u16,
1364 direction: Direction,
1365 ) {
1366 self.ellipsis_span = None;
1370
1371 let mut spans = mem::take(&mut self.spans);
1372
1373 let mut cached_spans = mem::take(&mut font_system.shape_buffer.spans);
1375 cached_spans.clear();
1376 cached_spans.extend(spans.drain(..).rev());
1377
1378 let bidi = unicode_bidi::BidiInfo::new(line, direction.bidi_level());
1379 let rtl = if bidi.paragraphs.is_empty() {
1380 direction == Direction::RightToLeft
1382 } else {
1383 bidi.paragraphs[0].level.is_rtl()
1384 };
1385
1386 log::trace!("Line {}: '{}'", if rtl { "RTL" } else { "LTR" }, line);
1387
1388 for para_info in &bidi.paragraphs {
1389 let line_range = para_info.range.clone();
1390 let levels = Self::adjust_levels(&unicode_bidi::Paragraph::new(&bidi, para_info));
1391
1392 let mut start = line_range.start;
1395 let mut run_level = levels[start];
1396 spans.reserve(line_range.end - start + 1);
1397
1398 for (i, &new_level) in levels
1399 .iter()
1400 .enumerate()
1401 .take(line_range.end)
1402 .skip(start + 1)
1403 {
1404 if new_level != run_level {
1405 let mut span = cached_spans.pop().unwrap_or_else(ShapeSpan::empty);
1407 span.build(
1408 font_system,
1409 line,
1410 attrs_list,
1411 start..i,
1412 rtl,
1413 run_level,
1414 shaping,
1415 );
1416 spans.push(span);
1417 start = i;
1418 run_level = new_level;
1419 }
1420 }
1421 let mut span = cached_spans.pop().unwrap_or_else(ShapeSpan::empty);
1422 span.build(
1423 font_system,
1424 line,
1425 attrs_list,
1426 start..line_range.end,
1427 rtl,
1428 run_level,
1429 shaping,
1430 );
1431 spans.push(span);
1432 }
1433
1434 let mut x = 0.0;
1436 for span in &mut spans {
1437 for word in &mut span.words {
1438 for glyph in &mut word.glyphs {
1439 if line.get(glyph.start..glyph.end) == Some("\t") {
1440 let tab_x_advance = f32::from(tab_width) * glyph.x_advance;
1442 let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance;
1443 glyph.x_advance = tab_stop - x;
1444 }
1445 x += glyph.x_advance;
1446 }
1447 }
1448 }
1449
1450 self.rtl = rtl;
1451 self.spans = spans;
1452 self.metrics_opt = attrs_list.defaults().metrics_opt.map(Into::into);
1453
1454 self.ellipsis_span.get_or_insert_with(|| {
1455 let attrs = if attrs_list.spans.is_empty() {
1456 attrs_list.defaults()
1457 } else {
1458 attrs_list.get_span(0) };
1462 let mut glyphs = shape_ellipsis(font_system, &attrs, shaping, rtl);
1463 if rtl {
1464 glyphs.reverse();
1465 }
1466 let word = ShapeWord {
1467 blank: false,
1468 glyphs,
1469 };
1470 let level = if rtl {
1473 unicode_bidi::Level::rtl()
1474 } else {
1475 unicode_bidi::Level::ltr()
1476 };
1477 ShapeSpan {
1478 level,
1479 words: vec![word],
1480 byte_range: 0..0,
1481 }
1482 });
1483
1484 font_system.shape_buffer.spans = cached_spans;
1486 }
1487
1488 fn adjust_levels(para: &unicode_bidi::Paragraph) -> Vec<unicode_bidi::Level> {
1490 use unicode_bidi::BidiClass::{B, BN, FSI, LRE, LRI, LRO, PDF, PDI, RLE, RLI, RLO, S, WS};
1491 let text = para.info.text;
1492 let levels = ¶.info.levels;
1493 let original_classes = ¶.info.original_classes;
1494
1495 let mut levels = levels.clone();
1496 let line_classes = &original_classes[..];
1497 let line_levels = &mut levels[..];
1498
1499 let mut reset_from: Option<usize> = Some(0);
1502 let mut reset_to: Option<usize> = None;
1503 for (i, c) in text.char_indices() {
1504 match line_classes[i] {
1505 RLE | LRE | RLO | LRO | PDF | BN => {}
1507 B | S => {
1509 assert_eq!(reset_to, None);
1510 reset_to = Some(i + c.len_utf8());
1511 if reset_from.is_none() {
1512 reset_from = Some(i);
1513 }
1514 }
1515 WS | FSI | LRI | RLI | PDI => {
1517 if reset_from.is_none() {
1518 reset_from = Some(i);
1519 }
1520 }
1521 _ => {
1522 reset_from = None;
1523 }
1524 }
1525 if let (Some(from), Some(to)) = (reset_from, reset_to) {
1526 for level in &mut line_levels[from..to] {
1527 *level = para.para.level;
1528 }
1529 reset_from = None;
1530 reset_to = None;
1531 }
1532 }
1533 if let Some(from) = reset_from {
1534 for level in &mut line_levels[from..] {
1535 *level = para.para.level;
1536 }
1537 }
1538 levels
1539 }
1540
1541 fn reorder(&self, line_range: &[VlRange]) -> Vec<Range<usize>> {
1543 let line: Vec<unicode_bidi::Level> = line_range.iter().map(|range| range.level).collect();
1544 let count = line.len();
1545 if count == 0 {
1546 return Vec::new();
1547 }
1548
1549 let mut elements: Vec<Range<usize>> = (0..count).map(|i| i..i + 1).collect();
1553
1554 let mut min_level = line[0];
1555 let mut max_level = line[0];
1556 for &level in &line[1..] {
1557 min_level = min(min_level, level);
1558 max_level = max(max_level, level);
1559 }
1560
1561 min_level = min_level.new_lowest_ge_rtl().expect("Level error");
1566
1567 while max_level >= min_level {
1568 let mut seq_start = 0;
1570 while seq_start < count {
1571 if line[elements[seq_start].start] < max_level {
1572 seq_start += 1;
1573 continue;
1574 }
1575
1576 let mut seq_end = seq_start + 1;
1578 while seq_end < count {
1579 if line[elements[seq_end].start] < max_level {
1580 break;
1581 }
1582 seq_end += 1;
1583 }
1584
1585 elements[seq_start..seq_end].reverse();
1587
1588 seq_start = seq_end;
1589 }
1590 max_level
1591 .lower(1)
1592 .expect("Lowering embedding level below zero");
1593 }
1594
1595 elements
1596 }
1597
1598 pub fn layout(
1599 &self,
1600 font_size: f32,
1601 width_opt: Option<f32>,
1602 wrap: Wrap,
1603 align: Option<Align>,
1604 match_mono_width: Option<f32>,
1605 hinting: Hinting,
1606 ) -> Vec<LayoutLine> {
1607 let mut lines = Vec::with_capacity(1);
1608 let mut scratch = ShapeBuffer::default();
1609 self.layout_to_buffer(
1610 &mut scratch,
1611 font_size,
1612 width_opt,
1613 wrap,
1614 Ellipsize::None,
1615 align,
1616 &mut lines,
1617 match_mono_width,
1618 hinting,
1619 );
1620 lines
1621 }
1622
1623 fn get_glyph_start_end(
1624 word: &ShapeWord,
1625 start: SpanWordGlyphPos,
1626 span_index: usize,
1627 word_idx: usize,
1628 _direction: LayoutDirection,
1629 congruent: bool,
1630 ) -> (usize, usize) {
1631 if span_index != start.span || word_idx != start.word {
1632 return (0, word.glyphs.len());
1633 }
1634 let (start_glyph_pos, end_glyph_pos) = if congruent {
1635 (start.glyph, word.glyphs.len())
1636 } else {
1637 (0, start.glyph)
1638 };
1639 (start_glyph_pos, end_glyph_pos)
1640 }
1641
1642 fn fit_glyphs(
1643 word: &ShapeWord,
1644 font_size: f32,
1645 start: SpanWordGlyphPos,
1646 span_index: usize,
1647 word_idx: usize,
1648 direction: LayoutDirection,
1649 congruent: bool,
1650 currently_used_width: f32,
1651 total_available_width: f32,
1652 forward: bool,
1653 ) -> (usize, f32) {
1654 let mut glyphs_w = 0.0;
1655 let (start_glyph_pos, end_glyph_pos) =
1656 Self::get_glyph_start_end(word, start, span_index, word_idx, direction, congruent);
1657
1658 if forward {
1659 let mut glyph_end = start_glyph_pos;
1660 for glyph_idx in start_glyph_pos..end_glyph_pos {
1661 let g_w = word.glyphs[glyph_idx].width(font_size);
1662 if currently_used_width + glyphs_w + g_w > total_available_width {
1663 break;
1664 }
1665 glyphs_w += g_w;
1666 glyph_end = glyph_idx + 1;
1667 }
1668 (glyph_end, glyphs_w)
1669 } else {
1670 let mut glyph_end = word.glyphs.len();
1671 for glyph_idx in (start_glyph_pos..end_glyph_pos).rev() {
1672 let g_w = word.glyphs[glyph_idx].width(font_size);
1673 if currently_used_width + glyphs_w + g_w > total_available_width {
1674 break;
1675 }
1676 glyphs_w += g_w;
1677 glyph_end = glyph_idx;
1678 }
1679 (glyph_end, glyphs_w)
1680 }
1681 }
1682
1683 #[inline]
1684 fn add_to_visual_line(
1685 &self,
1686 vl: &mut VisualLine,
1687 span_index: usize,
1688 start: WordGlyphPos,
1689 end: WordGlyphPos,
1690 width: f32,
1691 number_of_blanks: u32,
1692 ) {
1693 if end == start {
1694 return;
1695 }
1696
1697 vl.ranges.push(VlRange {
1698 span: span_index,
1699 start,
1700 end,
1701 level: self.spans[span_index].level,
1702 });
1703 vl.w += width;
1704 vl.spaces += number_of_blanks;
1705 }
1706
1707 fn remaining_content_exceeds(
1708 spans: &[ShapeSpan],
1709 font_size: f32,
1710 span_index: usize,
1711 word_idx: usize,
1712 word_count: usize,
1713 starting_word_index: usize,
1714 direction: LayoutDirection,
1715 congruent: bool,
1716 start_span: usize,
1717 span_count: usize,
1718 threshold: f32,
1719 ) -> bool {
1720 let mut acc: f32 = 0.0;
1721
1722 let word_range: Range<usize> = match (direction, congruent) {
1724 (LayoutDirection::Forward, true) => word_idx + 1..word_count,
1725 (LayoutDirection::Forward, false) => 0..word_idx,
1726 (LayoutDirection::Backward, true) => starting_word_index..word_idx,
1727 (LayoutDirection::Backward, false) => word_idx + 1..word_count,
1728 };
1729 for wi in word_range {
1730 acc += spans[span_index].words[wi].width(font_size);
1731 if acc > threshold {
1732 return true;
1733 }
1734 }
1735
1736 let span_range: Range<usize> = match direction {
1738 LayoutDirection::Forward => span_index + 1..span_count,
1739 LayoutDirection::Backward => start_span..span_index,
1740 };
1741 for si in span_range {
1742 for w in &spans[si].words {
1743 acc += w.width(font_size);
1744 if acc > threshold {
1745 return true;
1746 }
1747 }
1748 }
1749
1750 false
1751 }
1752
1753 #[inline]
1758 fn layout_spans(
1759 &self,
1760 current_visual_line: &mut VisualLine,
1761 font_size: f32,
1762 spans: &[ShapeSpan],
1763 start_opt: Option<SpanWordGlyphPos>,
1764 rtl: bool,
1765 width_opt: Option<f32>,
1766 ellipsize: Ellipsize,
1767 ellipsis_w: f32,
1768 direction: LayoutDirection,
1769 ) {
1770 let check_ellipsizing = matches!(ellipsize, Ellipsize::Start(_) | Ellipsize::End(_))
1771 && width_opt.is_some_and(|w| w.is_finite());
1772
1773 let max_width = width_opt.unwrap_or(f32::INFINITY);
1774 let span_count = spans.len();
1775
1776 let mut total_w: f32 = 0.0;
1777
1778 let start = if let Some(s) = start_opt {
1779 s
1780 } else {
1781 SpanWordGlyphPos::ZERO
1782 };
1783
1784 let span_indices: Vec<usize> = if matches!(direction, LayoutDirection::Forward) {
1785 (start.span..spans.len()).collect()
1786 } else {
1787 (start.span..spans.len()).rev().collect()
1788 };
1789
1790 'outer: for span_index in span_indices {
1791 let mut word_range_width = 0.;
1792 let mut number_of_blanks: u32 = 0;
1793
1794 let span = &spans[span_index];
1795 let word_count = span.words.len();
1796
1797 let starting_word_index = if span_index == start.span {
1798 start.word
1799 } else {
1800 0
1801 };
1802
1803 let congruent = rtl == span.level.is_rtl();
1804 let word_forward: bool = congruent == (direction == LayoutDirection::Forward);
1805
1806 let word_indices: Vec<usize> = match (direction, congruent, start_opt) {
1807 (LayoutDirection::Forward, true, _) => (starting_word_index..word_count).collect(),
1808 (LayoutDirection::Forward, false, Some(start)) => {
1809 if span_index == start.span {
1810 (0..start.word).rev().collect()
1811 } else {
1812 (0..word_count).rev().collect()
1813 }
1814 }
1815 (LayoutDirection::Forward, false, None) => (0..word_count).rev().collect(),
1816 (LayoutDirection::Backward, true, _) => {
1817 ((starting_word_index)..word_count).rev().collect()
1818 }
1819 (LayoutDirection::Backward, false, Some(start)) => {
1820 if span_index == start.span {
1821 if start.glyph > 0 {
1822 (0..(start.word + 1)).collect()
1823 } else {
1824 (0..(start.word)).collect()
1825 }
1826 } else {
1827 (0..word_count).collect()
1828 }
1829 }
1830 (LayoutDirection::Backward, false, None) => (0..span.words.len()).collect(),
1831 };
1832 for word_idx in word_indices {
1833 let word = &span.words[word_idx];
1834 let word_width = if span_index == start.span && word_idx == start.word {
1835 let (start_glyph_pos, end_glyph_pos) = Self::get_glyph_start_end(
1836 word, start, span_index, word_idx, direction, congruent,
1837 );
1838 let mut w = 0.;
1839 for glyph_idx in start_glyph_pos..end_glyph_pos {
1840 w += word.glyphs[glyph_idx].width(font_size);
1841 }
1842 w
1843 } else {
1844 word.width(font_size)
1845 };
1846
1847 let overflowing = {
1848 check_ellipsizing
1850 && (
1851 (total_w + word_range_width + word_width > max_width)
1853 || (Self::remaining_content_exceeds(
1854 spans,
1855 font_size,
1856 span_index,
1857 word_idx,
1858 word_count,
1859 starting_word_index,
1860 direction,
1861 congruent,
1862 start.span,
1863 span_count,
1864 ellipsis_w,
1865 ) && total_w + word_range_width + word_width + ellipsis_w
1866 > max_width)
1867 )
1868 };
1869
1870 if overflowing {
1871 let available = (max_width - ellipsis_w).max(0.0);
1873
1874 let (glyph_end, glyphs_w) = Self::fit_glyphs(
1875 word,
1876 font_size,
1877 start,
1878 span_index,
1879 word_idx,
1880 direction,
1881 congruent,
1882 total_w + word_range_width,
1883 available,
1884 word_forward,
1885 );
1886
1887 let (start_pos, end_pos) = if word_forward {
1888 if span_index == start.span {
1889 if !congruent {
1890 (WordGlyphPos::ZERO, WordGlyphPos::new(word_idx, glyph_end))
1891 } else {
1892 (
1893 start.word_glyph_pos(),
1894 WordGlyphPos::new(word_idx, glyph_end),
1895 )
1896 }
1897 } else {
1898 (WordGlyphPos::ZERO, WordGlyphPos::new(word_idx, glyph_end))
1899 }
1900 } else {
1901 let range_end = if span_index == start.span && !congruent {
1909 start.word_glyph_pos()
1910 } else {
1911 WordGlyphPos::new(span.words.len(), 0)
1912 };
1913 (WordGlyphPos::new(word_idx, glyph_end), range_end)
1914 };
1915 self.add_to_visual_line(
1916 current_visual_line,
1917 span_index,
1918 start_pos,
1919 end_pos,
1920 word_range_width + glyphs_w,
1921 number_of_blanks,
1922 );
1923
1924 current_visual_line.ellipsized = true;
1926 break 'outer;
1927 }
1928
1929 word_range_width += word_width;
1930 if word.blank {
1931 number_of_blanks += 1;
1932 }
1933
1934 if matches!(direction, LayoutDirection::Backward)
1936 && word_idx == start.word
1937 && span_index == start.span
1938 {
1939 let (start_pos, end_pos) = if word_forward {
1940 (WordGlyphPos::ZERO, start.word_glyph_pos())
1941 } else {
1942 (
1943 start.word_glyph_pos(),
1944 WordGlyphPos::new(span.words.len(), 0),
1945 )
1946 };
1947
1948 self.add_to_visual_line(
1949 current_visual_line,
1950 span_index,
1951 start_pos,
1952 end_pos,
1953 word_range_width,
1954 number_of_blanks,
1955 );
1956
1957 break 'outer;
1958 }
1959 }
1960
1961 total_w += word_range_width;
1964 let (start_pos, end_pos) = if congruent {
1965 if span_index == start.span {
1966 (
1967 start.word_glyph_pos(),
1968 WordGlyphPos::new(span.words.len(), 0),
1969 )
1970 } else {
1971 (WordGlyphPos::ZERO, WordGlyphPos::new(span.words.len(), 0))
1972 }
1973 } else if span_index == start.span && (start.word, start.glyph) != (0, 0) {
1974 (WordGlyphPos::ZERO, start.word_glyph_pos())
1977 } else {
1978 (WordGlyphPos::ZERO, WordGlyphPos::new(span.words.len(), 0))
1983 };
1984
1985 self.add_to_visual_line(
1986 current_visual_line,
1987 span_index,
1988 start_pos,
1989 end_pos,
1990 word_range_width,
1991 number_of_blanks,
1992 );
1993 }
1994
1995 if matches!(direction, LayoutDirection::Backward) {
1996 current_visual_line.ranges.reverse();
1997 }
1998 }
1999
2000 fn layout_middle(
2001 &self,
2002 current_visual_line: &mut VisualLine,
2003 font_size: f32,
2004 spans: &[ShapeSpan],
2005 start_opt: Option<SpanWordGlyphPos>,
2006 rtl: bool,
2007 width: f32,
2008 ellipsize: Ellipsize,
2009 ellipsis_w: f32,
2010 ) {
2011 assert!(matches!(ellipsize, Ellipsize::Middle(_)));
2012
2013 {
2015 let mut test_line = VisualLine::default();
2016 self.layout_spans(
2017 &mut test_line,
2018 font_size,
2019 spans,
2020 start_opt,
2021 rtl,
2022 Some(width),
2023 Ellipsize::End(EllipsizeHeightLimit::Lines(1)),
2024 ellipsis_w,
2025 LayoutDirection::Forward,
2026 );
2027 if !test_line.ellipsized && test_line.w <= width {
2028 *current_visual_line = test_line;
2029 return;
2030 }
2031 }
2032
2033 let mut starting_line = VisualLine::default();
2034 self.layout_spans(
2035 &mut starting_line,
2036 font_size,
2037 spans,
2038 start_opt,
2039 rtl,
2040 Some(width / 2.0),
2041 Ellipsize::End(EllipsizeHeightLimit::Lines(1)),
2042 0., LayoutDirection::Forward,
2044 );
2045 let forward_pass_overflowed = starting_line.ellipsized;
2046 let end_range_opt = starting_line.ranges.last();
2047 match end_range_opt {
2048 Some(range) if forward_pass_overflowed => {
2049 let congruent = rtl == self.spans[range.span].level.is_rtl();
2050 let mut ending_line = VisualLine::default();
2052 let start = if congruent {
2053 SpanWordGlyphPos {
2054 span: range.span,
2055 word: range.end.word,
2056 glyph: range.end.glyph,
2057 }
2058 } else {
2059 SpanWordGlyphPos {
2060 span: range.span,
2061 word: range.start.word,
2062 glyph: range.start.glyph,
2063 }
2064 };
2065 self.layout_spans(
2066 &mut ending_line,
2067 font_size,
2068 spans,
2069 Some(start),
2070 rtl,
2071 Some((width - starting_line.w - ellipsis_w).max(0.0)),
2072 Ellipsize::Start(EllipsizeHeightLimit::Lines(1)),
2073 0., LayoutDirection::Backward,
2075 );
2076 let ellipsis_level = self.ellipsis_level_between(
2079 starting_line.ranges.last(),
2080 ending_line.ranges.first(),
2081 );
2082 starting_line
2083 .ranges
2084 .push(self.ellipsis_vlrange(ellipsis_level));
2085 starting_line.ranges.extend(ending_line.ranges);
2086 current_visual_line.ranges = starting_line.ranges;
2087 current_visual_line.ellipsized = true;
2088 current_visual_line.w = starting_line.w + ending_line.w + ellipsis_w;
2089 current_visual_line.spaces = starting_line.spaces + ending_line.spaces;
2090 }
2091 None if forward_pass_overflowed && width > ellipsis_w => {
2092 current_visual_line
2095 .ranges
2096 .push(self.ellipsis_vlrange(if self.rtl {
2097 unicode_bidi::Level::rtl()
2098 } else {
2099 unicode_bidi::Level::ltr()
2100 }));
2101 current_visual_line.ellipsized = true;
2102 current_visual_line.w = ellipsis_w;
2103 current_visual_line.spaces = 0;
2104 }
2105 _ => {
2106 current_visual_line.ranges = starting_line.ranges;
2108 current_visual_line.w = starting_line.w;
2109 current_visual_line.spaces = starting_line.spaces;
2110 current_visual_line.ellipsized = false;
2111 }
2112 }
2113 }
2114
2115 fn get_span_words(&self, span_index: usize) -> &[ShapeWord] {
2117 if span_index == ELLIPSIS_SPAN {
2118 &self
2119 .ellipsis_span
2120 .as_ref()
2121 .expect("ellipsis_span not set")
2122 .words
2123 } else {
2124 &self.spans[span_index].words
2125 }
2126 }
2127
2128 fn byte_range_of_vlrange(&self, r: &VlRange) -> Option<(usize, usize)> {
2129 debug_assert_ne!(r.span, ELLIPSIS_SPAN);
2130 let words = self.get_span_words(r.span);
2131 let mut min_byte = usize::MAX;
2132 let mut max_byte = 0usize;
2133 let end_word = r.end.word + usize::from(r.end.glyph != 0);
2134 for (i, word) in words.iter().enumerate().take(end_word).skip(r.start.word) {
2135 let included_glyphs = match (i == r.start.word, i == r.end.word) {
2136 (false, false) => &word.glyphs[..],
2137 (true, false) => &word.glyphs[r.start.glyph..],
2138 (false, true) => &word.glyphs[..r.end.glyph],
2139 (true, true) => &word.glyphs[r.start.glyph..r.end.glyph],
2140 };
2141 for glyph in included_glyphs {
2142 min_byte = min_byte.min(glyph.start);
2143 max_byte = max_byte.max(glyph.end);
2144 }
2145 }
2146 if min_byte <= max_byte {
2147 Some((min_byte, max_byte))
2148 } else {
2149 None
2150 }
2151 }
2152
2153 fn compute_elided_byte_range(
2154 &self,
2155 visual_line: &VisualLine,
2156 line_len: usize,
2157 ) -> Option<(usize, usize)> {
2158 if !visual_line.ellipsized {
2159 return None;
2160 }
2161 let ellipsis_idx = visual_line
2163 .ranges
2164 .iter()
2165 .position(|r| r.span == ELLIPSIS_SPAN)?;
2166
2167 let before_end = (0..ellipsis_idx)
2169 .rev()
2170 .find_map(|i| self.byte_range_of_vlrange(&visual_line.ranges[i]))
2171 .map(|(_, end)| end)
2172 .unwrap_or(0);
2173
2174 let after_start = (ellipsis_idx + 1..visual_line.ranges.len())
2176 .find_map(|i| self.byte_range_of_vlrange(&visual_line.ranges[i]))
2177 .map(|(start, _)| start)
2178 .unwrap_or(line_len);
2179
2180 Some((before_end, after_start))
2181 }
2182
2183 fn max_byte_offset(&self) -> usize {
2186 self.spans
2187 .iter()
2188 .flat_map(|span| span.words.iter())
2189 .flat_map(|word| word.glyphs.iter())
2190 .map(|g| g.end)
2191 .max()
2192 .unwrap_or(0)
2193 }
2194
2195 fn ellipsis_w(&self, font_size: f32) -> f32 {
2197 self.ellipsis_span
2198 .as_ref()
2199 .map_or(0.0, |s| s.words.iter().map(|w| w.width(font_size)).sum())
2200 }
2201
2202 fn ellipsis_vlrange(&self, level: unicode_bidi::Level) -> VlRange {
2204 VlRange {
2205 span: ELLIPSIS_SPAN,
2206 start: WordGlyphPos::ZERO,
2207 end: WordGlyphPos::new(1, 0),
2208 level,
2209 }
2210 }
2211
2212 fn ellipsis_level_between(
2215 &self,
2216 before: Option<&VlRange>,
2217 after: Option<&VlRange>,
2218 ) -> unicode_bidi::Level {
2219 match (before, after) {
2220 (Some(a), Some(b)) if a.level == b.level => a.level,
2221 (Some(a), None) => a.level,
2222 (None, Some(b)) => b.level,
2223 _ => {
2224 if self.rtl {
2225 unicode_bidi::Level::rtl()
2226 } else {
2227 unicode_bidi::Level::ltr()
2228 }
2229 }
2230 }
2231 }
2232
2233 fn layout_line(
2234 &self,
2235 current_visual_line: &mut VisualLine,
2236 font_size: f32,
2237 spans: &[ShapeSpan],
2238 start_opt: Option<SpanWordGlyphPos>,
2239 rtl: bool,
2240 width_opt: Option<f32>,
2241 ellipsize: Ellipsize,
2242 ) {
2243 let ellipsis_w = self.ellipsis_w(font_size);
2244
2245 match (ellipsize, width_opt) {
2246 (Ellipsize::Start(_), Some(_)) => {
2247 self.layout_spans(
2248 current_visual_line,
2249 font_size,
2250 spans,
2251 start_opt,
2252 rtl,
2253 width_opt,
2254 ellipsize,
2255 ellipsis_w,
2256 LayoutDirection::Backward,
2257 );
2258 if current_visual_line.ellipsized {
2260 let level =
2261 self.ellipsis_level_between(None, current_visual_line.ranges.first());
2262 current_visual_line
2263 .ranges
2264 .insert(0, self.ellipsis_vlrange(level));
2265 current_visual_line.w += ellipsis_w;
2266 }
2267 }
2268 (Ellipsize::Middle(_), Some(width)) => {
2269 self.layout_middle(
2270 current_visual_line,
2271 font_size,
2272 spans,
2273 start_opt,
2274 rtl,
2275 width,
2276 ellipsize,
2277 ellipsis_w,
2278 );
2279 }
2280 _ => {
2281 self.layout_spans(
2282 current_visual_line,
2283 font_size,
2284 spans,
2285 start_opt,
2286 rtl,
2287 width_opt,
2288 ellipsize,
2289 ellipsis_w,
2290 LayoutDirection::Forward,
2291 );
2292 if current_visual_line.ellipsized {
2294 let level =
2295 self.ellipsis_level_between(current_visual_line.ranges.last(), None);
2296 current_visual_line
2297 .ranges
2298 .push(self.ellipsis_vlrange(level));
2299 current_visual_line.w += ellipsis_w;
2300 }
2301 }
2302 }
2303
2304 if current_visual_line.ellipsized {
2307 let line_len = self.max_byte_offset();
2308 current_visual_line.elided_byte_range =
2309 self.compute_elided_byte_range(current_visual_line, line_len);
2310 }
2311 }
2312
2313 pub(crate) fn fill_span_decorations(
2317 &self,
2318 attrs_list: &AttrsList,
2319 font_system: &mut FontSystem,
2320 ) {
2321 font_system.shape_buffer.span_decoration_data.clear();
2322 font_system.shape_buffer.span_decoration_offsets.clear();
2323 for span in &self.spans {
2324 let span_range = span.decorations_into(attrs_list, font_system);
2325 font_system
2326 .shape_buffer
2327 .span_decoration_offsets
2328 .push(span_range);
2329 }
2330 }
2331
2332 pub fn layout_to_buffer(
2333 &self,
2334 scratch: &mut ShapeBuffer,
2335 font_size: f32,
2336 width_opt: Option<f32>,
2337 wrap: Wrap,
2338 ellipsize: Ellipsize,
2339 align: Option<Align>,
2340 layout_lines: &mut Vec<LayoutLine>,
2341 match_mono_width: Option<f32>,
2342 hinting: Hinting,
2343 ) {
2344 let mut visual_lines = mem::take(&mut scratch.visual_lines);
2348 let mut cached_visual_lines = mem::take(&mut scratch.cached_visual_lines);
2349 cached_visual_lines.clear();
2350 cached_visual_lines.extend(visual_lines.drain(..).map(|mut l| {
2351 l.clear();
2352 l
2353 }));
2354
2355 let mut cached_glyph_sets = mem::take(&mut scratch.glyph_sets);
2357 cached_glyph_sets.clear();
2358 cached_glyph_sets.extend(layout_lines.drain(..).rev().map(|mut v| {
2359 v.glyphs.clear();
2360 v.glyphs
2361 }));
2362
2363 let mut current_visual_line = cached_visual_lines.pop().unwrap_or_default();
2368
2369 if wrap == Wrap::None {
2370 self.layout_line(
2371 &mut current_visual_line,
2372 font_size,
2373 &self.spans,
2374 None,
2375 self.rtl,
2376 width_opt,
2377 ellipsize,
2378 );
2379 } else {
2380 let mut total_line_height = 0.0;
2381 let mut total_line_count = 0;
2382 let max_line_count_opt = match ellipsize {
2383 Ellipsize::Start(EllipsizeHeightLimit::Lines(lines))
2384 | Ellipsize::Middle(EllipsizeHeightLimit::Lines(lines))
2385 | Ellipsize::End(EllipsizeHeightLimit::Lines(lines)) => Some(lines.max(1)),
2386 _ => None,
2387 };
2388 let max_height_opt = match ellipsize {
2389 Ellipsize::Start(EllipsizeHeightLimit::Height(height))
2390 | Ellipsize::Middle(EllipsizeHeightLimit::Height(height))
2391 | Ellipsize::End(EllipsizeHeightLimit::Height(height)) => Some(height),
2392 _ => None,
2393 };
2394 let line_height = self
2395 .metrics_opt
2396 .map_or_else(|| font_size, |m| m.line_height);
2397
2398 let try_ellipsize_last_line = |total_line_count: usize,
2399 total_line_height: f32,
2400 current_visual_line: &mut VisualLine,
2401 font_size: f32,
2402 start_opt: Option<SpanWordGlyphPos>,
2403 width_opt: Option<f32>,
2404 ellipsize: Ellipsize|
2405 -> bool {
2406 if max_line_count_opt == Some(total_line_count + 1)
2408 || max_height_opt.is_some_and(|max_height| {
2409 total_line_height + line_height * 2.0 > max_height
2410 })
2411 {
2412 self.layout_line(
2413 current_visual_line,
2414 font_size,
2415 &self.spans,
2416 start_opt,
2417 self.rtl,
2418 width_opt,
2419 ellipsize,
2420 );
2421 return true;
2422 }
2423 false
2424 };
2425
2426 if !try_ellipsize_last_line(
2427 total_line_count,
2428 total_line_height,
2429 &mut current_visual_line,
2430 font_size,
2431 None,
2432 width_opt,
2433 ellipsize,
2434 ) {
2435 'outer: for (span_index, span) in self.spans.iter().enumerate() {
2436 let mut word_range_width = 0.;
2437 let mut width_before_last_blank = 0.;
2438 let mut number_of_blanks: u32 = 0;
2439
2440 if self.rtl != span.level.is_rtl() {
2442 let mut fitting_start = WordGlyphPos::new(span.words.len(), 0);
2444 for (i, word) in span.words.iter().enumerate().rev() {
2445 let word_width = word.width(font_size);
2446 if current_visual_line.w + (word_range_width + word_width)
2450 <= width_opt.unwrap_or(f32::INFINITY)
2451 || (word.blank
2454 && (current_visual_line.w + word_range_width) <= width_opt.unwrap_or(f32::INFINITY))
2455 {
2456 if word.blank {
2458 number_of_blanks += 1;
2459 width_before_last_blank = word_range_width;
2460 }
2461 word_range_width += word_width;
2462 } else if wrap == Wrap::Glyph
2463 || (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
2465 {
2466 if word_range_width > 0.
2468 && wrap == Wrap::WordOrGlyph
2469 && word_width > width_opt.unwrap_or(f32::INFINITY)
2470 {
2471 self.add_to_visual_line(
2472 &mut current_visual_line,
2473 span_index,
2474 WordGlyphPos::new(i + 1, 0),
2475 fitting_start,
2476 word_range_width,
2477 number_of_blanks,
2478 );
2479
2480 visual_lines.push(current_visual_line);
2481 current_visual_line =
2482 cached_visual_lines.pop().unwrap_or_default();
2483
2484 number_of_blanks = 0;
2485 word_range_width = 0.;
2486
2487 fitting_start = WordGlyphPos::new(i, 0);
2488 total_line_count += 1;
2489 total_line_height += line_height;
2490 if try_ellipsize_last_line(
2491 total_line_count,
2492 total_line_height,
2493 &mut current_visual_line,
2494 font_size,
2495 Some(SpanWordGlyphPos::with_wordglyph(
2496 span_index,
2497 fitting_start,
2498 )),
2499 width_opt,
2500 ellipsize,
2501 ) {
2502 break 'outer;
2503 }
2504 }
2505
2506 for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
2507 let glyph_width = glyph.width(font_size);
2508 if current_visual_line.w + (word_range_width + glyph_width)
2509 <= width_opt.unwrap_or(f32::INFINITY)
2510 {
2511 word_range_width += glyph_width;
2512 } else {
2513 self.add_to_visual_line(
2514 &mut current_visual_line,
2515 span_index,
2516 WordGlyphPos::new(i, glyph_i + 1),
2517 fitting_start,
2518 word_range_width,
2519 number_of_blanks,
2520 );
2521 visual_lines.push(current_visual_line);
2522 current_visual_line =
2523 cached_visual_lines.pop().unwrap_or_default();
2524
2525 number_of_blanks = 0;
2526 word_range_width = glyph_width;
2527 fitting_start = WordGlyphPos::new(i, glyph_i + 1);
2528 total_line_count += 1;
2529 total_line_height += line_height;
2530 if try_ellipsize_last_line(
2531 total_line_count,
2532 total_line_height,
2533 &mut current_visual_line,
2534 font_size,
2535 Some(SpanWordGlyphPos::with_wordglyph(
2536 span_index,
2537 fitting_start,
2538 )),
2539 width_opt,
2540 ellipsize,
2541 ) {
2542 break 'outer;
2543 }
2544 }
2545 }
2546 } else {
2547 if word_range_width > 0. {
2551 let trailing_blank = span
2554 .words
2555 .get(i + 1)
2556 .is_some_and(|previous_word| previous_word.blank);
2557
2558 if trailing_blank {
2559 number_of_blanks = number_of_blanks.saturating_sub(1);
2560 self.add_to_visual_line(
2561 &mut current_visual_line,
2562 span_index,
2563 WordGlyphPos::new(i + 2, 0),
2564 fitting_start,
2565 width_before_last_blank,
2566 number_of_blanks,
2567 );
2568 } else {
2569 self.add_to_visual_line(
2570 &mut current_visual_line,
2571 span_index,
2572 WordGlyphPos::new(i + 1, 0),
2573 fitting_start,
2574 word_range_width,
2575 number_of_blanks,
2576 );
2577 }
2578 }
2579
2580 if !current_visual_line.ranges.is_empty() {
2583 visual_lines.push(current_visual_line);
2584 current_visual_line =
2585 cached_visual_lines.pop().unwrap_or_default();
2586 number_of_blanks = 0;
2587 total_line_count += 1;
2588 total_line_height += line_height;
2589
2590 if try_ellipsize_last_line(
2591 total_line_count,
2592 total_line_height,
2593 &mut current_visual_line,
2594 font_size,
2595 Some(SpanWordGlyphPos::with_wordglyph(
2596 span_index,
2597 if word.blank {
2598 WordGlyphPos::new(i, 0)
2599 } else {
2600 WordGlyphPos::new(i + 1, 0)
2601 },
2602 )),
2603 width_opt,
2604 ellipsize,
2605 ) {
2606 break 'outer;
2607 }
2608 }
2609
2610 if word.blank {
2611 word_range_width = 0.;
2612 fitting_start = WordGlyphPos::new(i, 0);
2613 } else {
2614 word_range_width = word_width;
2615 fitting_start = WordGlyphPos::new(i + 1, 0);
2616 }
2617 }
2618 }
2619 self.add_to_visual_line(
2620 &mut current_visual_line,
2621 span_index,
2622 WordGlyphPos::new(0, 0),
2623 fitting_start,
2624 word_range_width,
2625 number_of_blanks,
2626 );
2627 } else {
2628 let mut fitting_start = WordGlyphPos::ZERO;
2630 for (i, word) in span.words.iter().enumerate() {
2631 let word_width = word.width(font_size);
2632 if current_visual_line.w + (word_range_width + word_width)
2633 <= width_opt.unwrap_or(f32::INFINITY)
2634 || (word.blank
2637 && (current_visual_line.w + word_range_width) <= width_opt.unwrap_or(f32::INFINITY))
2638 {
2639 if word.blank {
2641 number_of_blanks += 1;
2642 width_before_last_blank = word_range_width;
2643 }
2644 word_range_width += word_width;
2645 } else if wrap == Wrap::Glyph
2646 || (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
2648 {
2649 if word_range_width > 0.
2651 && wrap == Wrap::WordOrGlyph
2652 && word_width > width_opt.unwrap_or(f32::INFINITY)
2653 {
2654 self.add_to_visual_line(
2655 &mut current_visual_line,
2656 span_index,
2657 fitting_start,
2658 WordGlyphPos::new(i, 0),
2659 word_range_width,
2660 number_of_blanks,
2661 );
2662
2663 visual_lines.push(current_visual_line);
2664 current_visual_line =
2665 cached_visual_lines.pop().unwrap_or_default();
2666
2667 number_of_blanks = 0;
2668 word_range_width = 0.;
2669
2670 fitting_start = WordGlyphPos::new(i, 0);
2671 total_line_count += 1;
2672 total_line_height += line_height;
2673 if try_ellipsize_last_line(
2674 total_line_count,
2675 total_line_height,
2676 &mut current_visual_line,
2677 font_size,
2678 Some(SpanWordGlyphPos::with_wordglyph(
2679 span_index,
2680 fitting_start,
2681 )),
2682 width_opt,
2683 ellipsize,
2684 ) {
2685 break 'outer;
2686 }
2687 }
2688
2689 for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
2690 let glyph_width = glyph.width(font_size);
2691 if current_visual_line.w + (word_range_width + glyph_width)
2692 <= width_opt.unwrap_or(f32::INFINITY)
2693 {
2694 word_range_width += glyph_width;
2695 } else {
2696 self.add_to_visual_line(
2697 &mut current_visual_line,
2698 span_index,
2699 fitting_start,
2700 WordGlyphPos::new(i, glyph_i),
2701 word_range_width,
2702 number_of_blanks,
2703 );
2704 visual_lines.push(current_visual_line);
2705 current_visual_line =
2706 cached_visual_lines.pop().unwrap_or_default();
2707
2708 number_of_blanks = 0;
2709 word_range_width = glyph_width;
2710 fitting_start = WordGlyphPos::new(i, glyph_i);
2711 total_line_count += 1;
2712 total_line_height += line_height;
2713 if try_ellipsize_last_line(
2714 total_line_count,
2715 total_line_height,
2716 &mut current_visual_line,
2717 font_size,
2718 Some(SpanWordGlyphPos::with_wordglyph(
2719 span_index,
2720 fitting_start,
2721 )),
2722 width_opt,
2723 ellipsize,
2724 ) {
2725 break 'outer;
2726 }
2727 }
2728 }
2729 } else {
2730 if word_range_width > 0. {
2734 let trailing_blank = i > 0 && span.words[i - 1].blank;
2737
2738 if trailing_blank {
2739 number_of_blanks = number_of_blanks.saturating_sub(1);
2740 self.add_to_visual_line(
2741 &mut current_visual_line,
2742 span_index,
2743 fitting_start,
2744 WordGlyphPos::new(i - 1, 0),
2745 width_before_last_blank,
2746 number_of_blanks,
2747 );
2748 } else {
2749 self.add_to_visual_line(
2750 &mut current_visual_line,
2751 span_index,
2752 fitting_start,
2753 WordGlyphPos::new(i, 0),
2754 word_range_width,
2755 number_of_blanks,
2756 );
2757 }
2758 }
2759
2760 if !current_visual_line.ranges.is_empty() {
2761 visual_lines.push(current_visual_line);
2762 current_visual_line =
2763 cached_visual_lines.pop().unwrap_or_default();
2764 number_of_blanks = 0;
2765 total_line_count += 1;
2766 total_line_height += line_height;
2767 if try_ellipsize_last_line(
2768 total_line_count,
2769 total_line_height,
2770 &mut current_visual_line,
2771 font_size,
2772 Some(SpanWordGlyphPos::with_wordglyph(
2773 span_index,
2774 WordGlyphPos::new(i, 0),
2775 )),
2776 width_opt,
2777 ellipsize,
2778 ) {
2779 break 'outer;
2780 }
2781 }
2782
2783 if word.blank {
2784 word_range_width = 0.;
2785 fitting_start = WordGlyphPos::new(i + 1, 0);
2786 } else {
2787 word_range_width = word_width;
2788 fitting_start = WordGlyphPos::new(i, 0);
2789 }
2790 }
2791 }
2792 self.add_to_visual_line(
2793 &mut current_visual_line,
2794 span_index,
2795 fitting_start,
2796 WordGlyphPos::new(span.words.len(), 0),
2797 word_range_width,
2798 number_of_blanks,
2799 );
2800 }
2801 }
2802 }
2803 }
2804
2805 if current_visual_line.ranges.is_empty() {
2806 current_visual_line.clear();
2807 cached_visual_lines.push(current_visual_line);
2808 } else {
2809 visual_lines.push(current_visual_line);
2810 }
2811
2812 let align = align.unwrap_or(if self.rtl { Align::Right } else { Align::Left });
2814
2815 let line_width = width_opt.unwrap_or_else(|| {
2816 let mut width: f32 = 0.0;
2817 for visual_line in &visual_lines {
2818 width = width.max(visual_line.w);
2819 }
2820 width
2821 });
2822
2823 let start_x = if self.rtl { line_width } else { 0.0 };
2824
2825 let number_of_visual_lines = visual_lines.len();
2826 for (index, visual_line) in visual_lines.iter().enumerate() {
2827 if visual_line.ranges.is_empty() {
2828 continue;
2829 }
2830
2831 let new_order = self.reorder(&visual_line.ranges);
2832
2833 let mut glyphs = cached_glyph_sets
2834 .pop()
2835 .unwrap_or_else(|| Vec::with_capacity(1));
2836 let mut x = start_x;
2837 let mut y = 0.;
2838 let mut max_ascent: f32 = 0.;
2839 let mut max_descent: f32 = 0.;
2840 let alignment_correction = match (align, self.rtl) {
2841 (Align::Left, true) => (line_width - visual_line.w).max(0.),
2842 (Align::Left, false) => 0.,
2843 (Align::Right, true) => 0.,
2844 (Align::Right, false) => (line_width - visual_line.w).max(0.),
2845 (Align::Center, _) => (line_width - visual_line.w).max(0.) / 2.0,
2846 (Align::End, _) => (line_width - visual_line.w).max(0.),
2847 (Align::Justified, _) => 0.,
2848 };
2849
2850 if self.rtl {
2851 x -= alignment_correction;
2852 } else {
2853 x += alignment_correction;
2854 }
2855
2856 if hinting == Hinting::Enabled {
2857 x = x.round();
2858 }
2859
2860 let justification_expansion = if matches!(align, Align::Justified)
2876 && visual_line.spaces > 0
2877 && index != number_of_visual_lines - 1
2879 {
2880 (line_width - visual_line.w) / visual_line.spaces as f32
2881 } else {
2882 0.
2883 };
2884
2885 let elided_byte_range = if visual_line.ellipsized {
2886 visual_line.elided_byte_range
2887 } else {
2888 None
2889 };
2890
2891 let mut decorations: Vec<DecorationSpan> = Vec::new();
2892
2893 let process_range = |range: Range<usize>,
2894 x: &mut f32,
2895 y: &mut f32,
2896 glyphs: &mut Vec<LayoutGlyph>,
2897 decorations: &mut Vec<DecorationSpan>,
2898 max_ascent: &mut f32,
2899 max_descent: &mut f32| {
2900 for r in visual_line.ranges[range.clone()].iter() {
2901 let is_ellipsis = r.span == ELLIPSIS_SPAN;
2902 let span_words = self.get_span_words(r.span);
2903 let deco_spans: &[(Range<usize>, GlyphDecorationData)] = if is_ellipsis {
2904 &[]
2905 } else {
2906 scratch
2907 .span_decoration_offsets
2908 .get(r.span)
2909 .map_or(&[][..], |range| {
2910 &scratch.span_decoration_data[range.start..range.end]
2911 })
2912 };
2913 let mut deco_cursor: usize = 0;
2916 for i in r.start.word..r.end.word + usize::from(r.end.glyph != 0) {
2918 let word = &span_words[i];
2919 let included_glyphs = match (i == r.start.word, i == r.end.word) {
2920 (false, false) => &word.glyphs[..],
2921 (true, false) => &word.glyphs[r.start.glyph..],
2922 (false, true) => &word.glyphs[..r.end.glyph],
2923 (true, true) => &word.glyphs[r.start.glyph..r.end.glyph],
2924 };
2925
2926 for glyph in included_glyphs {
2927 let font_size = glyph.metrics_opt.map_or(font_size, |x| x.font_size);
2929
2930 let match_mono_em_width = match_mono_width.map(|w| w / font_size);
2931
2932 let glyph_font_size = match (
2933 match_mono_em_width,
2934 glyph.font_monospace_em_width,
2935 ) {
2936 (Some(match_em_width), Some(glyph_em_width))
2937 if glyph_em_width != match_em_width =>
2938 {
2939 let glyph_to_match_factor = glyph_em_width / match_em_width;
2940 let glyph_font_size = math::roundf(glyph_to_match_factor)
2941 .max(1.0)
2942 / glyph_to_match_factor
2943 * font_size;
2944 log::trace!(
2945 "Adjusted glyph font size ({font_size} => {glyph_font_size})"
2946 );
2947 glyph_font_size
2948 }
2949 _ => font_size,
2950 };
2951
2952 let mut x_advance = glyph_font_size * glyph.x_advance
2953 + if word.blank {
2954 justification_expansion
2955 } else {
2956 0.0
2957 };
2958 if let Some(match_em_width) = match_mono_em_width {
2959 x_advance = ((x_advance / match_em_width).round()) * match_em_width;
2961 }
2962 if hinting == Hinting::Enabled {
2963 x_advance = x_advance.round();
2964 }
2965 if self.rtl {
2966 *x -= x_advance;
2967 }
2968 let y_advance = glyph_font_size * glyph.y_advance;
2969 let mut layout_glyph = glyph.layout(
2970 glyph_font_size,
2971 glyph.metrics_opt.map(|x| x.line_height),
2972 *x,
2973 *y,
2974 x_advance,
2975 r.level,
2976 );
2977 if is_ellipsis {
2982 if let Some((elided_start, elided_end)) = elided_byte_range {
2983 let boundary = if elided_start == 0 {
2989 elided_end
2990 } else {
2991 elided_start
2992 };
2993 layout_glyph.start = boundary;
2994 layout_glyph.end = boundary;
2995 }
2996 }
2997 glyphs.push(layout_glyph);
2998
2999 if deco_cursor >= deco_spans.len()
3000 || glyph.start < deco_spans[deco_cursor].0.start
3001 {
3002 deco_cursor = 0;
3003 }
3004 while deco_cursor < deco_spans.len()
3005 && deco_spans[deco_cursor].0.end <= glyph.start
3006 {
3007 deco_cursor += 1;
3008 }
3009 let glyph_deco = deco_spans
3010 .get(deco_cursor)
3011 .filter(|(range, _)| glyph.start >= range.start);
3012 let glyph_idx = glyphs.len() - 1;
3013 let extends = matches!(
3017 (decorations.last(), &glyph_deco),
3018 (Some(span), Some((_, d))) if span.data == *d && span.glyph_range.end == glyph_idx
3019 );
3020 if extends {
3021 if let Some(last) = decorations.last_mut() {
3022 last.glyph_range.end = glyph_idx + 1;
3023 }
3024 } else if let Some((_, d)) = glyph_deco {
3025 decorations.push(DecorationSpan {
3026 glyph_range: glyph_idx..glyph_idx + 1,
3027 data: d.clone(),
3028 color_opt: glyphs[glyph_idx].color_opt,
3029 font_size: glyphs[glyph_idx].font_size,
3030 });
3031 }
3032 if !self.rtl {
3033 *x += x_advance;
3034 }
3035 *y += y_advance;
3036 *max_ascent = max_ascent.max(glyph_font_size * glyph.ascent);
3037 *max_descent = max_descent.max(glyph_font_size * glyph.descent);
3038 }
3039 }
3040 }
3041 };
3042
3043 if self.rtl {
3044 for range in new_order.into_iter().rev() {
3045 process_range(
3046 range,
3047 &mut x,
3048 &mut y,
3049 &mut glyphs,
3050 &mut decorations,
3051 &mut max_ascent,
3052 &mut max_descent,
3053 );
3054 }
3055 } else {
3056 for range in new_order {
3058 process_range(
3059 range,
3060 &mut x,
3061 &mut y,
3062 &mut glyphs,
3063 &mut decorations,
3064 &mut max_ascent,
3065 &mut max_descent,
3066 );
3067 }
3068 }
3069
3070 let mut line_height_opt: Option<f32> = None;
3071 for glyph in &glyphs {
3072 if let Some(glyph_line_height) = glyph.line_height_opt {
3073 line_height_opt = line_height_opt
3074 .map_or(Some(glyph_line_height), |line_height| {
3075 Some(line_height.max(glyph_line_height))
3076 });
3077 }
3078 }
3079
3080 layout_lines.push(LayoutLine {
3081 w: if align != Align::Justified {
3082 visual_line.w
3083 } else if self.rtl {
3084 start_x - x
3085 } else {
3086 x
3087 },
3088 max_ascent,
3089 max_descent,
3090 line_height_opt,
3091 glyphs,
3092 decorations,
3093 });
3094 }
3095
3096 if layout_lines.is_empty() {
3098 layout_lines.push(LayoutLine {
3099 w: 0.0,
3100 max_ascent: 0.0,
3101 max_descent: 0.0,
3102 line_height_opt: self.metrics_opt.map(|x| x.line_height),
3103 glyphs: Vec::default(),
3104 decorations: Vec::new(),
3105 });
3106 }
3107
3108 scratch.visual_lines = visual_lines;
3110 scratch.visual_lines.append(&mut cached_visual_lines);
3111 scratch.cached_visual_lines = cached_visual_lines;
3112 scratch.glyph_sets = cached_glyph_sets;
3113 }
3114}