1use std::ops::Range;
2
3use cranpose_ui_graphics::{MemoryHeadroom, whole};
4use smallvec::SmallVec;
5
6use crate::{
7 bidi_level::{BidiLevel, reorder_visually},
8 ellipsis_at::{ELLIPSIS, EllipsisAt, ellipsis_at, take_rest_of_paragraph},
9 font_family_resolver::FontFamilyResolver,
10 glyph_ink::{InkReach, line_band},
11 index_range::narrow,
12 itemizer::{Itemizer, TextItem},
13 laid_glyphs::LaidGlyphs,
14 layout_body::LayoutBody,
15 layout_input::LayoutInput,
16 layout_options::TextLayoutOptions,
17 line_box::{LastLineBox, LineBoxInputs},
18 line_breaker::{
19 BreakKind, BrokenLine, LineBalancer, LineBreaker, LineEnd, WrapWidths, Wrapping,
20 break_lines, hard_break, has_hard_break, starts_paragraph, visible_end,
21 },
22 line_hold::{end_ellipsis_hold, fitting_hold, wrapped_hold},
23 line_runs::{line_extent, place_runs},
24 line_text::{LineDepth, LineEdges, LineText, before_hard_break, indent},
25 paragraph::{LineBreak, ResolvedTextDirection},
26 paragraph_intrinsics::ParagraphIntrinsics,
27 paragraph_measures::ParagraphMeasures,
28 script::Script,
29 shaped_glyphs::ShapedGlyphs,
30 shaped_item::{
31 Piece, ShapedItem, face_index, hyphen, item_at, part_glyphs, scale, set_cluster,
32 shape_inserted, spaced_width,
33 },
34 shaper::Shaper,
35 text_layout::{LayoutLine, TextLayout},
36 typeface::Typeface,
37};
38#[cfg(feature = "hyphenation")]
39use crate::{
40 hyphenator::Hyphenator,
41 line_breaker::BreakOpportunity,
42 paragraph::{Hyphens, TextAlign},
43};
44
45#[derive(Default)]
48pub struct TextLayouter {
49 itemizer: Itemizer,
50 shaper: Shaper,
51 breaker: LineBreaker,
52 balancer: LineBalancer,
53 items: Vec<ShapedItem>,
54 measures: ParagraphMeasures,
57 lines: Vec<BrokenLine>,
58 pieces: Vec<Piece>,
59 glyphs: ShapedGlyphs,
62 faces: SmallVec<[Typeface; 1]>,
65 line_box: LastLineBox,
66 #[cfg(feature = "hyphenation")]
67 hyphenator: Hyphenator,
68 #[cfg(feature = "hyphenation")]
71 hyphenated: Vec<BreakOpportunity>,
72 #[cfg(feature = "hyphenation")]
73 points: Vec<usize>,
74}
75
76const SCRATCH_GLYPHS: usize = 4096;
79const PARAGRAPH_LAST_LINE: f32 = 0.16;
82
83impl TextLayouter {
84 pub fn layout(&mut self, input: &LayoutInput<'_>, resolver: &FontFamilyResolver) -> TextLayout {
86 self.layout_taking(input, resolver, None)
87 }
88
89 pub fn relayout(
93 &mut self,
94 input: &LayoutInput<'_>,
95 resolver: &FontFamilyResolver,
96 shaped: &TextLayout,
97 ) -> TextLayout {
98 self.layout_taking(input, resolver, Some(shaped))
99 }
100
101 fn layout_taking(
102 &mut self,
103 input: &LayoutInput<'_>,
104 resolver: &FontFamilyResolver,
105 shaped: Option<&TextLayout>,
106 ) -> TextLayout {
107 let mut faces = SmallVec::new();
108 self.shape(input, resolver, shaped, &mut faces);
109 self.place(input, resolver, shaped, faces)
110 }
111
112 #[inline(never)]
116 fn place(
117 &mut self,
118 input: &LayoutInput<'_>,
119 resolver: &FontFamilyResolver,
120 shaped: Option<&TextLayout>,
121 faces: SmallVec<[Typeface; 1]>,
122 ) -> TextLayout {
123 let mut body = LayoutBody {
124 faces,
125 ..LayoutBody::default()
126 };
127 let width = self.fill(input, resolver, shaped, &mut body);
128 body.shrink_to_fit();
130 let reach = self.shaper.take_ink_reach();
131 TextLayout::new(body, width, (input.min_width, input.max_width), reach)
132 }
133
134 pub fn layout_into(
137 &mut self,
138 input: &LayoutInput<'_>,
139 resolver: &FontFamilyResolver,
140 layout: &mut TextLayout,
141 ) {
142 layout.refill((input.min_width, input.max_width), |body| {
143 body.clear();
144 self.shape(input, resolver, None, &mut body.faces);
145 let width = self.fill(input, resolver, None, body);
146 (width, self.shaper.take_ink_reach())
147 });
148 }
149
150 #[inline(never)]
154 fn fill(
155 &mut self,
156 input: &LayoutInput<'_>,
157 resolver: &FontFamilyResolver,
158 shaped: Option<&TextLayout>,
159 layout: &mut LayoutBody,
160 ) -> f32 {
161 let options = input.options.normalized();
162 layout.shaped = self.glyphs.len();
163 layout.glyphs = shaped
164 .and_then(|shaped| shaped.body().shaped_glyphs())
165 .cloned()
166 .unwrap_or_default();
167 let lengths = input.lengths;
168 let text = input.text;
169 let mut lone = (self.items.len() == 1
172 && !has_hard_break(text)
173 && visible_end(text, 0, text.len()) == text.len())
174 .then(|| self.lone_piece(input, layout));
175 let unbroken = if let Some(piece) = &lone {
178 Some(piece.width)
179 } else {
180 self.measure_widths(input, &layout.faces);
181 (!has_hard_break(text)).then(|| {
182 self.measures
183 .widths
184 .width(0, visible_end(text, 0, text.len()))
185 })
186 };
187 let widest = unbroken.map_or_else(
188 || self.widest_paragraph(input),
189 |width| width.max(0.0) + lengths.first_line_indent,
190 );
191 layout.content_width = whole::ceil(widest * lengths.grid) / lengths.grid;
192 layout.follows_max = options.soft_wrap || options.overflow.is_ellipsis();
193 let width = layout.wrap_width(input.min_width, input.max_width);
194 let wrap = WrapWidths {
195 width: if options.soft_wrap {
196 width
197 } else {
198 f32::INFINITY
199 },
200 first_indent: lengths.first_line_indent,
201 rest_indent: lengths.rest_line_indent,
202 };
203 let fits = unbroken
206 .map(|line| line + wrap.first_indent)
207 .filter(|&line| line <= wrap.width);
208 if let Some(line) = fits.filter(|&line| line <= width)
211 && let Some(piece) = lone.take()
212 {
213 let mut hold = fitting_hold(&options, line);
214 if ellipsis_at(&options).is_some() {
215 hold.narrow(line, f32::INFINITY);
216 }
217 layout.hold = Some(hold);
218 self.lay_out_one_line(input, &options, resolver, piece, width, layout);
219 return width;
220 }
221 if lone.is_some() {
222 self.measure_widths(input, &layout.faces);
223 }
224 self.fill_lines(input, (&options, wrap, width), resolver, fits, layout);
225 width
226 }
227
228 #[inline(never)]
234 fn fill_lines(
235 &mut self,
236 input: &LayoutInput<'_>,
237 (options, wrap, width): (&TextLayoutOptions, WrapWidths, f32),
238 resolver: &FontFamilyResolver,
239 fits: Option<f32>,
240 layout: &mut LayoutBody,
241 ) {
242 let text = input.text;
243 if let Some(line) = fits {
244 layout.hold = Some(fitting_hold(options, line));
245 self.lines.clear();
246 self.lines.push(BrokenLine::plain(0..text.len()));
247 } else {
248 self.wrap_lines(input, options, wrap, layout);
249 }
250 layout.did_overflow_height = self.lines.len() > options.max_lines;
251 if layout.did_overflow_height && ellipsis_at(options).is_some() {
252 take_rest_of_paragraph(&mut self.lines, text, options.max_lines - 1);
253 }
254 self.lines.truncate(options.max_lines);
255 self.build_lines(input, options, resolver, width, layout);
256 }
257
258 fn wrap_lines(
261 &mut self,
262 input: &LayoutInput<'_>,
263 options: &TextLayoutOptions,
264 wrap: WrapWidths,
265 layout: &mut LayoutBody,
266 ) {
267 let text = input.text;
268 let line_break = input.paragraph.line_break;
269 self.breaker
270 .opportunities(text, line_break, &mut self.measures.breaks);
271 #[cfg(feature = "hyphenation")]
272 let breaks =
273 if input.paragraph.hyphens == Hyphens::Auto && self.hyphenate(input, wrap, layout) {
274 &self.hyphenated
275 } else {
276 &self.measures.breaks
277 };
278 #[cfg(not(feature = "hyphenation"))]
279 let breaks = &self.measures.breaks;
280 let wrapping = Wrapping {
281 text,
282 widths: &self.measures.widths,
283 wrap,
284 };
285 match line_break {
286 LineBreak::Paragraph => {
287 self.balancer
288 .balance(&wrapping, breaks, PARAGRAPH_LAST_LINE, &mut self.lines);
289 }
290 LineBreak::Heading => {
291 self.balancer
292 .balance(&wrapping, breaks, 1.0, &mut self.lines);
293 }
294 LineBreak::Simple | LineBreak::Unspecified => {
295 break_lines(&wrapping, breaks, &mut self.lines);
296 }
297 }
298 layout.hold = wrapped_hold(&self.lines, input, options, &wrapping, breaks);
299 }
300
301 pub fn intrinsics(
304 &mut self,
305 input: &LayoutInput<'_>,
306 resolver: &FontFamilyResolver,
307 ) -> ParagraphIntrinsics {
308 let mut faces = std::mem::take(&mut self.faces);
309 faces.clear();
310 self.shape(input, resolver, None, &mut faces);
311 self.measure_widths(input, &faces);
312 self.faces = faces;
313 self.keep_glyph_room();
314 let text = input.text;
315 self.breaker
316 .opportunities(text, input.paragraph.line_break, &mut self.measures.breaks);
317 let max_intrinsic_width = self.widest_paragraph(input);
318 let empty_last = self
321 .measures
322 .breaks
323 .last()
324 .is_none_or(|opportunity| opportunity.kind == BreakKind::Hard);
325 let mut min_intrinsic_width = if empty_last {
326 input.lengths.first_line_indent
327 } else {
328 0.0
329 };
330 let mut word = 0;
331 for opportunity in &self.measures.breaks {
332 let width = self
333 .measures
334 .widths
335 .width(word, visible_end(text, word, opportunity.offset));
336 min_intrinsic_width = min_intrinsic_width.max(width + indent(input, word));
337 word = opportunity.offset;
338 }
339 ParagraphIntrinsics {
340 min_intrinsic_width,
341 max_intrinsic_width,
342 }
343 }
344
345 fn measure_widths(&mut self, input: &LayoutInput<'_>, faces: &[Typeface]) {
348 self.measures
349 .widths
350 .measure(&self.items, input, faces, &self.glyphs);
351 }
352
353 fn keep_glyph_room(&mut self) {
356 if self.glyphs.capacity() > SCRATCH_GLYPHS {
357 self.glyphs = ShapedGlyphs::default();
358 }
359 }
360
361 fn finish_lines(
366 &mut self,
367 input: &LayoutInput<'_>,
368 options: &TextLayoutOptions,
369 bottom: LineDepth,
370 layout: &mut LayoutBody,
371 ) {
372 if layout.glyphs.len() != self.glyphs.len() {
374 layout.glyphs = LaidGlyphs::new(&self.glyphs);
375 }
376 self.keep_glyph_room();
377 let grid = input.lengths.grid;
380 layout.height = if layout.lines.len() < options.min_lines {
381 let missing = options.min_lines - layout.lines.len();
382 (bottom.pixels + whole::round(layout.advance * grid) * lines_f32(missing)) / grid
383 } else {
384 bottom.layout
385 };
386 layout.line_width = whole::ceil(layout.widest * grid) / grid;
387 let align = input.paragraph.text_align;
389 if !layout.keeps_lines_at_indent(align) {
390 let width = layout.width_between(input.min_width, input.max_width);
391 layout.align_lines(width, align);
392 }
393 }
394
395 fn widest_paragraph(&self, input: &LayoutInput<'_>) -> f32 {
398 let text = input.text;
399 let mut widest: f32 = 0.0;
400 let mut start = 0;
401 for (at, ch) in text.char_indices() {
402 if hard_break(ch) {
403 widest = widest.max(
404 self.measures
405 .widths
406 .width(start, visible_end(text, start, at)),
407 );
408 start = at + ch.len_utf8();
409 }
410 }
411 let last = self
412 .measures
413 .widths
414 .width(start, visible_end(text, start, text.len()));
415 widest.max(last) + input.lengths.first_line_indent
416 }
417
418 pub fn set_memory_headroom(&mut self, headroom: MemoryHeadroom) {
421 self.shaper.words.set_headroom(headroom);
422 }
423
424 #[cfg(feature = "hyphenation")]
426 pub fn hyphenator_mut(&mut self) -> &mut Hyphenator {
427 &mut self.hyphenator
428 }
429
430 #[cfg(feature = "hyphenation")]
436 fn hyphenate(
437 &mut self,
438 input: &LayoutInput<'_>,
439 wrap: WrapWidths,
440 layout: &mut LayoutBody,
441 ) -> bool {
442 if !wrap.width.is_finite() {
443 return false;
444 }
445 let first = wrap.width - wrap.first_indent;
446 let wider_than = match input.paragraph.line_break {
447 LineBreak::Paragraph | LineBreak::Heading => f32::NEG_INFINITY,
448 LineBreak::Simple | LineBreak::Unspecified => first.min(wrap.width - wrap.rest_indent),
449 };
450 let justified = if input.paragraph.text_align == TextAlign::Justify {
453 0.25
454 } else {
455 1.0
456 };
457 let penalty_per_size = 0.5 * first * justified;
458 self.hyphenated.clear();
459 let mut word_start = 0;
460 for index in 0..self.measures.breaks.len() {
461 let opportunity = self.measures.breaks[index];
462 let word_end = visible_end(input.text, word_start, opportunity.offset);
463 if self.measures.widths.width(word_start, word_end) > wider_than {
464 self.hyphenate_word(input, word_start..word_end, penalty_per_size, layout);
465 }
466 self.hyphenated.push(opportunity);
467 word_start = opportunity.offset;
468 }
469 true
470 }
471
472 #[cfg(feature = "hyphenation")]
476 fn hyphenate_word(
477 &mut self,
478 input: &LayoutInput<'_>,
479 word: Range<usize>,
480 penalty_per_size: f32,
481 layout: &mut LayoutBody,
482 ) {
483 let Some(text) = input.text.get(word.clone()) else {
484 return;
485 };
486 let letters = text.trim_start_matches(|ch: char| !ch.is_alphabetic());
487 let start = word.start + (text.len() - letters.len());
488 let letters = letters.trim_end_matches(|ch: char| !ch.is_alphabetic());
489 let Some(first) = self.items.get(item_at(&self.items, start)) else {
490 return;
491 };
492 if letters.is_empty() || first.level.is_rtl() {
493 return;
494 }
495 self.points.clear();
496 self.hyphenator
497 .break_points(input.spans[first.span].language, letters, &mut self.points);
498 let mut hyphen_width: Option<(usize, f32)> = None;
499 for &point in &self.points {
500 let at = start + point;
501 if self.measures.widths.next_boundary(at.saturating_sub(1)) != Some(at) {
502 continue;
503 }
504 let index = item_at(&self.items, at.saturating_sub(1));
505 let item = &self.items[index];
506 let width = match hyphen_width {
507 Some((known, width)) if known == index => width,
508 _ => {
509 let face = &layout.faces[item.face];
510 let shaped = self.glyphs.len();
511 let text = hyphen(face);
512 let item = item.inserted(text, face);
513 let width =
514 shape_inserted(&mut self.shaper, input, &item, text, &mut self.glyphs);
515 self.glyphs.truncate(shaped);
516 hyphen_width = Some((index, width));
517 width
518 }
519 };
520 self.hyphenated.push(BreakOpportunity {
521 offset: at,
522 kind: BreakKind::Hyphen {
523 width,
524 penalty: penalty_per_size * input.spans[item.span].size,
525 },
526 });
527 }
528 }
529
530 fn shape(
534 &mut self,
535 input: &LayoutInput<'_>,
536 resolver: &FontFamilyResolver,
537 shaped: Option<&TextLayout>,
538 faces: &mut SmallVec<[Typeface; 1]>,
539 ) {
540 self.items.clear();
541 self.glyphs.clear();
542 self.shaper.take_ink_reach();
543 self.glyphs.reserve(input.text.len());
545 let text = input.text;
546 let (shaper, items, glyphs) = (&mut self.shaper, &mut self.items, &mut self.glyphs);
547 let direction = input.paragraph.text_direction;
548 let mut taken = shaped.map(|shaped| (shaped.body(), 0));
549 self.itemizer
550 .itemize(text, direction, input.spans, resolver, |item| {
551 let start = glyphs.len();
552 match &mut taken {
553 Some((body, from)) => {
554 *from = body.glyphs.take(*from..body.shaped, &item.range, glyphs);
555 shaper.note_ink_reach(body.ink_reach);
556 shaper.note_synthesis(&item, &input.spans[item.span]);
557 }
558 None => {
559 shaper.shape(text, &item, &input.spans[item.span], 0..text.len(), glyphs);
560 }
561 }
562 items.push(ShapedItem {
563 text: item.range,
564 glyphs: start..glyphs.len(),
565 face: face_index(faces, item.typeface),
566 span: item.span,
567 level: item.level,
568 paragraph_level: item.paragraph_level,
569 script: item.script,
570 spacing: if item.script.takes_letter_spacing() {
571 let span = &input.spans[item.span];
572 span.letter_spacing
573 .unwrap_or_else(|| item.typeface.tracking(span.size))
574 } else {
575 0.0
576 },
577 });
578 });
579 if self.items.iter().any(|item| item.spacing < 0.0) {
581 self.shaper.note_ink_reach(InkReach::Lines);
582 }
583 }
584
585 fn build_lines(
586 &mut self,
587 input: &LayoutInput<'_>,
588 options: &TextLayoutOptions,
589 resolver: &FontFamilyResolver,
590 width: f32,
591 layout: &mut LayoutBody,
592 ) {
593 let mut top = LineDepth::default();
594 let last = self.lines.len() - 1;
595 layout.lines.reserve_exact(self.lines.len());
597 layout.runs.reserve_exact(self.lines.len());
598 for index in 0..=last {
599 let range = self.lines[index].text.clone();
600 let indent = indent(input, range.start);
601 let line = LineText {
602 visible: visible_end(input.text, range.start, range.end),
603 drawn: before_hard_break(input.text, range.start, range.end),
604 indent,
605 width,
606 range,
607 };
608 let ellipsized = self.collect_line(input, options, resolver, (index, &line), layout);
609 reorder_visually(&mut self.pieces, |piece| piece.level);
610 let edges = LineEdges {
611 top,
612 first: index == 0,
613 last: index == last,
614 ellipsized,
615 };
616 let reach;
617 (top, reach) = self.push_line(input, resolver, line, edges, layout);
618 self.shaper.note_ink_reach(reach);
619 }
620 for index in 1..layout.lines.len() {
621 let starts = starts_paragraph(input.text, layout.lines[index].text.start());
622 layout.lines[index - 1].ends_paragraph = starts;
623 }
624 self.finish_lines(input, options, top, layout);
625 }
626
627 fn lone_piece(&self, input: &LayoutInput<'_>, layout: &LayoutBody) -> Piece {
629 let item = &self.items[0];
630 let span = &input.spans[item.span];
631 let scale = scale(span, &layout.faces[item.face]);
632 Piece {
633 width: spaced_width(&self.glyphs[item.glyphs.clone()], scale, item.spacing),
634 text: item.text.clone(),
635 glyphs: item.glyphs.clone(),
636 face: item.face,
637 size: span.size,
638 scale,
639 spacing: item.spacing,
640 baseline_shift: span.baseline_shift,
641 level: item.level,
642 }
643 }
644
645 fn lay_out_one_line(
648 &mut self,
649 input: &LayoutInput<'_>,
650 options: &TextLayoutOptions,
651 resolver: &FontFamilyResolver,
652 piece: Piece,
653 width: f32,
654 layout: &mut LayoutBody,
655 ) {
656 self.pieces.clear();
657 self.pieces.push(piece);
658 let end = input.text.len();
659 let indent = input.lengths.first_line_indent;
660 let line = LineText {
661 range: 0..end,
662 visible: end,
663 drawn: end,
664 indent,
665 width,
666 };
667 let edges = LineEdges {
668 top: LineDepth::default(),
669 first: true,
670 last: true,
671 ellipsized: false,
672 };
673 let (bottom, reach) = self.push_line(input, resolver, line, edges, layout);
674 self.shaper.note_ink_reach(reach);
675 self.finish_lines(input, options, bottom, layout);
676 }
677
678 fn push_line(
681 &self,
682 input: &LayoutInput<'_>,
683 resolver: &FontFamilyResolver,
684 line: LineText,
685 edges: LineEdges,
686 layout: &mut LayoutBody,
687 ) -> (LineDepth, InkReach) {
688 let (runs, width) = place_runs(&self.pieces, line.visible, layout);
689 let extent = line_extent(&self.pieces, input, resolver, layout, line.range.start);
690 let asked = input.lengths.line_height.unwrap_or(f32::NAN);
691 let (top, grid) = (edges.top, input.lengths.grid);
692 let inputs = LineBoxInputs::of(input.paragraph, extent, asked, grid);
693 let line_box = self.line_box.of(inputs);
694 let trim_top = if edges.first { line_box.trim_top } else { 0.0 };
695 let trim_bottom = if edges.last {
696 line_box.trim_bottom
697 } else {
698 0.0
699 };
700 if edges.first {
701 layout.advance = line_box.height / grid;
702 }
703 let baseline = (top.pixels + line_box.baseline - trim_top) / grid;
705 let bottom = LineDepth::at(top.pixels + line_box.height - trim_top - trim_bottom, grid);
706 let level = self.paragraph_level(input, line.range.start);
707 layout.rtl |= level.is_rtl();
708 layout.lines.push(LayoutLine {
709 level,
710 text: line.range.into(),
711 visible_end: narrow(line.visible),
712 runs: runs.into(),
713 top: top.layout,
714 baseline,
715 bottom: bottom.layout,
716 left: line.indent,
717 indent: line.indent,
718 width,
719 stretch: 0.0,
720 ellipsized: edges.ellipsized,
721 ends_paragraph: true,
722 });
723 layout.widest = layout.widest.max(width + line.indent);
724 let band = || line_band(&self.pieces, &layout.faces);
725 let height = bottom.layout - top.layout;
726 let reach = InkReach::of_line(baseline - top.layout, height, (extent, band));
727 (bottom, reach)
728 }
729
730 fn collect_line(
735 &mut self,
736 input: &LayoutInput<'_>,
737 options: &TextLayoutOptions,
738 resolver: &FontFamilyResolver,
739 (index, line): (usize, &LineText),
740 layout: &mut LayoutBody,
741 ) -> bool {
742 self.pieces.clear();
743 let last = index + 1 == self.lines.len();
744 let fitting = self.measures.widths.width(line.range.start, line.visible) + line.indent;
747 let overflows = fitting > line.width;
748 let ellipsis = ellipsis_at(options).filter(|_| last);
749 let shown = ellipsis.filter(|_| overflows || (last && layout.did_overflow_height));
750 if let Some(hold) = layout.hold.as_mut().filter(|_| ellipsis.is_some()) {
751 if shown.is_none() {
752 hold.narrow(fitting, f32::INFINITY);
753 } else if overflows {
754 hold.narrow(f32::NEG_INFINITY, fitting);
755 }
756 }
757 let ellipsized = match shown {
758 Some(at) => self.ellipsize(input, resolver, at, line, layout),
759 None => false,
760 };
761 if !ellipsized {
762 self.collect_pieces(input, line.range.start..line.drawn, line.visible, layout);
763 if self.lines[index].end == LineEnd::Hyphen {
764 self.push_hyphen(input, line.range.end, layout);
765 }
766 }
767 ellipsized
768 }
769
770 fn ellipsize(
774 &mut self,
775 input: &LayoutInput<'_>,
776 resolver: &FontFamilyResolver,
777 at: EllipsisAt,
778 line: &LineText,
779 layout: &mut LayoutBody,
780 ) -> bool {
781 let (range, visible) = (line.range.clone(), line.visible);
782 let span = input
784 .spans
785 .iter()
786 .position(|span| span.range.contains(&range.start))
787 .unwrap_or(0);
788 let level = self.paragraph_level(input, range.start);
789 let Some(mut ellipsis) = self.ellipsis_piece(input, resolver, span, level, layout) else {
790 return false;
791 };
792 let room = (line.width - line.indent - ellipsis.width).max(0.0);
793 let text = input.text;
794 let (head, tail) = match at {
795 EllipsisAt::End => {
796 let edge = ellipsis.width + line.indent;
797 let cut = self
798 .measures
799 .widths
800 .last_fitting(range.start, visible, edge, line.width);
801 if let Some(hold) = &mut layout.hold {
802 let kept = end_ellipsis_hold(&self.measures.widths, text, line, cut, edge);
803 hold.narrow(kept.min, kept.max);
804 }
805 (visible_end(text, range.start, cut), range.end)
806 }
807 EllipsisAt::Start => {
808 layout.hold = None;
809 (
810 range.start,
811 self.measures
812 .widths
813 .first_fitting(range.start, visible, room),
814 )
815 }
816 EllipsisAt::Middle => {
817 layout.hold = None;
820 let tail = self
821 .measures
822 .widths
823 .first_fitting(range.start, visible, room / 2.0);
824 let used = self.measures.widths.width(tail, visible);
825 (
826 self.measures
827 .widths
828 .last_fitting(range.start, tail, 0.0, room - used),
829 tail,
830 )
831 }
832 };
833 set_cluster(&mut self.glyphs.glyphs_mut()[ellipsis.glyphs.clone()], head);
834 ellipsis.text = head..head;
835 self.collect_pieces(input, range.start..head, visible, layout);
836 self.pieces.push(ellipsis);
837 if tail < line.drawn {
838 self.collect_pieces(input, tail..line.drawn, visible, layout);
839 }
840 true
841 }
842
843 fn ellipsis_piece(
845 &mut self,
846 input: &LayoutInput<'_>,
847 resolver: &FontFamilyResolver,
848 span: usize,
849 level: BidiLevel,
850 layout: &mut LayoutBody,
851 ) -> Option<Piece> {
852 let style = &input.spans[span];
853 let mut typeface = None;
854 resolver.typeface_runs(ELLIPSIS, style.font, |_, face| {
855 typeface.get_or_insert(face);
856 });
857 let typeface = &resolver.typefaces()[typeface?];
858 let item = TextItem {
859 range: 0..ELLIPSIS.len(),
860 level,
861 paragraph_level: level,
862 script: Script::COMMON,
863 typeface,
864 span,
865 };
866 let start = self.glyphs.len();
867 let width = shape_inserted(&mut self.shaper, input, &item, ELLIPSIS, &mut self.glyphs);
868 Some(Piece {
869 width,
870 text: 0..0,
871 glyphs: start..self.glyphs.len(),
872 face: face_index(&mut layout.faces, typeface),
873 size: style.size,
874 scale: scale(style, typeface),
875 spacing: 0.0,
876 baseline_shift: style.baseline_shift,
877 level,
878 })
879 }
880
881 fn push_hyphen(&mut self, input: &LayoutInput<'_>, at: usize, layout: &mut LayoutBody) {
884 let Some(item) = self.items.get(item_at(&self.items, at.saturating_sub(1))) else {
885 return;
886 };
887 let span = &input.spans[item.span];
888 let face = &layout.faces[item.face];
889 let hyphen = hyphen(face);
890 let start = self.glyphs.len();
891 let width = shape_inserted(
892 &mut self.shaper,
893 input,
894 &item.inserted(hyphen, face),
895 hyphen,
896 &mut self.glyphs,
897 );
898 set_cluster(&mut self.glyphs.glyphs_mut()[start..], at);
899 self.pieces.push(Piece {
900 width,
901 text: at..at,
902 glyphs: start..self.glyphs.len(),
903 face: item.face,
904 size: span.size,
905 scale: scale(span, face),
906 spacing: 0.0,
907 baseline_shift: span.baseline_shift,
908 level: item.level,
909 });
910 }
911
912 fn collect_pieces(
916 &mut self,
917 input: &LayoutInput<'_>,
918 part: Range<usize>,
919 visible: usize,
920 layout: &mut LayoutBody,
921 ) {
922 let Self {
923 items,
924 shaper,
925 pieces,
926 glyphs: all,
927 ..
928 } = self;
929 let first = items.partition_point(|item| item.text.end <= part.start);
930 for item in &items[first..] {
931 if item.text.start >= part.end {
932 break;
933 }
934 let start = item.text.start.max(part.start);
935 let end = item.text.end.min(part.end);
936 let middle = visible.clamp(start, end);
937 for (sub, level) in [
938 (start..middle, item.level),
939 (middle..end, item.paragraph_level),
940 ] {
941 if sub.is_empty() {
942 continue;
943 }
944 let glyphs = part_glyphs(
945 shaper,
946 input,
947 item,
948 sub.clone(),
949 part.clone(),
950 &layout.faces,
951 all,
952 );
953 let scale = scale(&input.spans[item.span], &layout.faces[item.face]);
954 pieces.push(Piece {
955 width: spaced_width(&all[glyphs.clone()], scale, item.spacing),
956 text: sub,
957 glyphs,
958 face: item.face,
959 size: input.spans[item.span].size,
960 scale,
961 spacing: item.spacing,
962 baseline_shift: input.spans[item.span].baseline_shift,
963 level,
964 });
965 }
966 }
967 }
968
969 fn paragraph_level(&self, input: &LayoutInput<'_>, offset: usize) -> BidiLevel {
971 self.items
972 .get(item_at(&self.items, offset))
973 .or_else(|| self.items.last())
974 .map_or_else(
975 || match input.paragraph.text_direction.resolve("") {
976 ResolvedTextDirection::Ltr => BidiLevel::LTR,
977 ResolvedTextDirection::Rtl => BidiLevel::RTL,
978 },
979 |item| item.paragraph_level,
980 )
981 }
982}
983
984fn lines_f32(count: usize) -> f32 {
986 f32::from(u16::try_from(count).unwrap_or(u16::MAX))
987}