1use std::cell::RefCell;
2use std::rc::Rc;
3
4use crate::media::text_formatting::formatting_object_pool::{FormattingObjectPool, RentedList};
5use crate::media::text_formatting::text_line_impl::TextLineImpl;
6use crate::media::text_formatting::text_run_cache::CachedShapingResult;
7use crate::media::text_formatting::unicode::{
8 BidiAlgorithm, BidiData, Codepoint, GraphemeEnumerator, LineBreak, LineBreakEnumerator,
9};
10use crate::media::text_formatting::wrapping_text_line_break::WrappingTextLineBreak;
11use crate::media::text_formatting::{
12 GlyphInfo, ITextSource, ShapedBuffer, ShapedTextRun, SplitResult, TextCharacters,
13 TextFormatter, TextLine, TextLineBreak, TextParagraphProperties, TextRun, TextRunCache, TextRunProperties,
14 TextShaper, TextShaperOptions, UnshapedTextRun, DEFAULT_TEXT_SOURCE_LENGTH,
15};
16use crate::media::{FlowDirection, FontManager, TextWrapping};
17use crate::utilities::{MathUtilities, ReadOnlyMemory};
18
19const DEFAULT_TEXT: [u16; DEFAULT_TEXT_SOURCE_LENGTH as usize] = [b'a' as u16; DEFAULT_TEXT_SOURCE_LENGTH as usize];
20
21crate::ferro_thread_local! {
22 static EMPTY: ReadOnlyMemory<u16> = ReadOnlyMemory::from_vec(vec![b' ' as u16]);
23
24 static BIDI: RefCell<Option<Box<(BidiData, BidiAlgorithm)>>> = const { RefCell::new(None) };
26}
27
28pub struct TextFormatterImpl;
32
33impl TextFormatterImpl {
34 pub fn new() -> Self {
35 Self
36 }
37
38 fn format_line_from_cache(
40 cached: &CachedShapingResult,
41 first_text_source_index: i32,
42 paragraph_width: f64,
43 paragraph_properties: &Rc<dyn TextParagraphProperties>,
44 object_pool: &FormattingObjectPool,
45 ) -> Rc<TextLineImpl> {
46 let resolved_flow_direction = cached.resolved_flow_direction;
47
48 let next_line_break = cached
49 .text_end_of_line
50 .as_ref()
51 .map(|text_end_of_line| Rc::new(TextLineBreak::new(Some(text_end_of_line.clone()), resolved_flow_direction, false)));
52
53 match paragraph_properties.text_wrapping() {
54 TextWrapping::NoWrap => {
55 let line_runs = Self::add_ref_shaped_runs(&cached.shaped_runs);
56
57 let text_line = TextLineImpl::new(
58 line_runs,
59 first_text_source_index,
60 cached.text_source_length,
61 paragraph_width,
62 paragraph_properties.clone(),
63 resolved_flow_direction,
64 next_line_break,
65 false,
66 );
67
68 text_line.finalize_line();
69
70 text_line
71 }
72 TextWrapping::WrapWithOverflow | TextWrapping::Wrap => {
73 let mut runs = Self::add_ref_shaped_runs(&cached.shaped_runs);
74
75 Self::perform_text_wrapping(
76 &mut runs,
77 false,
78 first_text_source_index,
79 paragraph_width,
80 paragraph_properties,
81 resolved_flow_direction,
82 next_line_break.as_ref(),
83 object_pool,
84 )
85 }
86 }
87 }
88
89 fn add_ref_shaped_runs(runs: &[Rc<dyn TextRun>]) -> Vec<Rc<dyn TextRun>> {
92 let mut result = Vec::with_capacity(runs.len());
93
94 for run in runs {
95 if let Some(shaped) = run.downcast_ref::<ShapedTextRun>() {
96 shaped.add_reference();
97 }
98
99 result.push(run.clone());
100 }
101
102 result
103 }
104
105 pub(crate) fn split_text_runs(
110 text_runs: &[Rc<dyn TextRun>],
111 length: i32,
112 object_pool: &FormattingObjectPool,
113 ) -> SplitResult<RentedList<Rc<dyn TextRun>>> {
114 Self::split_text_runs_with_length(text_runs, length, object_pool).0
115 }
116
117 pub fn split_text_runs_with_length(
124 text_runs: &[Rc<dyn TextRun>],
125 length: i32,
126 object_pool: &FormattingObjectPool,
127 ) -> (SplitResult<RentedList<Rc<dyn TextRun>>>, i32) {
128 if length == 0 {
129 let mut second = object_pool.text_run_lists.rent();
130
131 second.extend(text_runs.iter().cloned());
132
133 return (SplitResult::new(None, Some(second)), 0);
134 }
135
136 let mut first = object_pool.text_run_lists.rent();
137 let mut current_length = 0;
138
139 for i in 0..text_runs.len() {
140 let current_run = &text_runs[i];
141 let current_run_length = current_run.length();
142
143 if current_length + current_run_length < length {
144 current_length += current_run_length;
145
146 continue;
147 }
148
149 let first_count = if current_run_length >= 1 { i + 1 } else { i };
150
151 if first_count > 1 {
152 first.extend(text_runs[..i].iter().cloned());
153 }
154
155 let mut second_count = text_runs.len() - first_count;
156
157 if current_length + current_run_length == length {
158 let second = if second_count > 0 {
159 let mut second = object_pool.text_run_lists.rent();
160
161 let offset = if current_run_length >= 1 { 1 } else { 0 };
162
163 for j in 0..second_count {
164 second.push(text_runs[i + j + offset].clone());
165 }
166
167 Some(second)
168 } else {
169 None
170 };
171
172 first.push(current_run.clone());
173
174 return (SplitResult::new(Some(first), second), current_length + current_run_length);
175 } else {
176 second_count += 1;
177
178 let mut second = object_pool.text_run_lists.rent();
179 let mut added_first_length = 0;
180 let trailing_loop_start;
181
182 if let Some(shaped_text_characters) = current_run.downcast_ref::<ShapedTextRun>() {
183 let split = shaped_text_characters.split(length - current_length);
184
185 if let Some(split_first) = split.first {
186 added_first_length = split_first.length();
187 first.push(split_first);
188 }
189
190 if let Some(split_second) = split.second {
191 second.push(split_second);
192 }
193
194 shaped_text_characters.dispose();
197
198 trailing_loop_start = 1;
201 } else if current_length == 0 {
202 first.push(current_run.clone());
208 added_first_length = current_run_length;
209 trailing_loop_start = 1;
210 } else {
211 trailing_loop_start = 0;
217 }
218
219 for j in trailing_loop_start..second_count {
220 second.push(text_runs[i + j].clone());
221 }
222
223 return (SplitResult::new(Some(first), Some(second)), current_length + added_first_length);
224 }
225 }
226
227 first.extend(text_runs.iter().cloned());
228
229 (SplitResult::new(Some(first), None), current_length)
230 }
231
232 fn shape_text_runs(
237 text_runs: &[Rc<dyn TextRun>],
238 paragraph_properties: &dyn TextParagraphProperties,
239 object_pool: &FormattingObjectPool,
240 font_manager: &FontManager,
241 ) -> (RentedList<Rc<dyn TextRun>>, FlowDirection) {
242 let flow_direction = paragraph_properties.flow_direction();
243 let mut shaped_runs = object_pool.text_run_lists.rent();
244
245 if text_runs.is_empty() {
246 return (shaped_runs, flow_direction);
247 }
248
249 let mut bidi = BIDI
252 .with(|slot| slot.borrow_mut().take())
253 .unwrap_or_else(|| Box::new((BidiData::new(), BidiAlgorithm::new())));
254
255 let (bidi_data, bidi_algorithm) = &mut *bidi;
256
257 bidi_data.reset();
258 bidi_data.set_paragraph_embedding_level(flow_direction as i8);
259
260 for text_run in text_runs {
261 let text = text_run.text_span();
262
263 if !text.is_empty() {
264 bidi_data.append(text);
265 } else if text_run.length() == DEFAULT_TEXT_SOURCE_LENGTH {
266 bidi_data.append(&DEFAULT_TEXT);
267 } else {
268 bidi_data.append(&vec![b'a' as u16; text_run.length().max(0) as usize]);
269 }
270 }
271
272 bidi_algorithm.process(bidi_data);
273
274 let resolved_embedding_level = bidi_algorithm.resolve_embedding_level(bidi_data.classes());
275
276 let resolved_flow_direction =
277 if (resolved_embedding_level & 1) == 0 { FlowDirection::LeftToRight } else { FlowDirection::RightToLeft };
278
279 let mut processed_runs = object_pool.text_run_lists.rent();
280 let mut grouped_runs = object_pool.unshaped_text_run_lists.rent();
281
282 Self::coalesce_levels(text_runs, bidi_algorithm.resolved_levels(), font_manager, &mut processed_runs);
283
284 bidi_data.reset();
285 bidi_algorithm.reset();
286
287 BIDI.with(|slot| *slot.borrow_mut() = Some(bidi));
288
289 let text_shaper = TextShaper::current();
290
291 let mut index = 0;
292
293 while index < processed_runs.len() {
294 let current_run = &processed_runs[index];
295
296 match current_run.clone().downcast_rc::<UnshapedTextRun>() {
297 Some(mut shapeable_run) => {
298 grouped_runs.clear();
299 grouped_runs.push(shapeable_run.clone());
300
301 let mut text = shapeable_run.text();
302 let properties = shapeable_run.run_properties().clone();
303
304 while index + 1 < processed_runs.len() {
305 let Some(next_run) = processed_runs[index + 1].clone().downcast_rc::<UnshapedTextRun>() else {
306 break;
307 };
308
309 if shapeable_run.bidi_level() == next_run.bidi_level() {
310 if let Some(joined_text) = Self::try_join_contiguous_memories(&text, &next_run.text()) {
311 if Self::can_shape_together(&*properties, &**next_run.run_properties()) {
312 grouped_runs.push(next_run.clone());
313 index += 1;
314 shapeable_run = next_run;
315 text = joined_text;
316 continue;
317 }
318 }
319 }
320
321 break;
322 }
323
324 let shaper_options = TextShaperOptions::with_all(
325 properties.cached_glyph_typeface(),
326 properties.font_rendering_em_size(),
327 shapeable_run.bidi_level(),
328 properties.culture_info().cloned(),
329 paragraph_properties.default_incremental_tab(),
330 paragraph_properties.letter_spacing(),
331 properties.font_features().map(|features| Rc::new(features.to_vec())),
332 );
333
334 Self::shape_together(&grouped_runs, &text, &shaper_options, &text_shaper, &mut shaped_runs);
335 }
336 None => {
337 shaped_runs.push(current_run.clone());
338 }
339 }
340
341 index += 1;
342 }
343
344 object_pool.text_run_lists.return_list(processed_runs);
345 object_pool.unshaped_text_run_lists.return_list(grouped_runs);
346
347 (shaped_runs, resolved_flow_direction)
348 }
349
350 fn try_join_contiguous_memories(x: &ReadOnlyMemory<u16>, y: &ReadOnlyMemory<u16>) -> Option<ReadOnlyMemory<u16>> {
355 if !x.shares_owner_with(y) {
356 return None;
357 }
358
359 let owner = x.owner()?;
360
361 let x_range = (x.offset_in_owner(), x.len());
362 let y_range = (y.offset_in_owner(), y.len());
363
364 let (first_range, second_range) = if x_range.0 <= y_range.0 { (x_range, y_range) } else { (y_range, x_range) };
365
366 if first_range.0 + first_range.1 == second_range.0 {
367 return Some(ReadOnlyMemory::with_range(owner.clone(), first_range.0, x_range.1 + y_range.1));
368 }
369
370 None
371 }
372
373 fn can_shape_together(x: &dyn TextRunProperties, y: &dyn TextRunProperties) -> bool {
374 MathUtilities::are_close(x.font_rendering_em_size(), y.font_rendering_em_size())
375 && x.typeface() == y.typeface()
376 && x.baseline_alignment() == y.baseline_alignment()
377 }
378
379 fn shape_together(
380 text_runs: &[Rc<UnshapedTextRun>],
381 text: &ReadOnlyMemory<u16>,
382 options: &TextShaperOptions,
383 text_shaper: &TextShaper,
384 results: &mut Vec<Rc<dyn TextRun>>,
385 ) {
386 let mut shaped_buffer = text_shaper.shape_text(text, options);
387
388 let mut previous_length = 0;
389
390 for current_run in text_runs {
391 let split_result = shaped_buffer.split(previous_length + current_run.length());
392
393 match split_result.first {
399 Some(first) if !first.text().is_empty() => {
400 previous_length = 0;
401
402 results.push(Rc::new(ShapedTextRun::new(first, current_run.run_properties().clone())));
403 }
404 _ => {
405 previous_length += current_run.length();
406 }
407 }
408
409 match split_result.second {
410 None => return,
411 Some(second) => shaped_buffer = second,
412 }
413 }
414 }
415
416 fn coalesce_levels(
423 text_characters: &[Rc<dyn TextRun>],
424 levels: &[i8],
425 font_manager: &FontManager,
426 processed_runs: &mut Vec<Rc<dyn TextRun>>,
427 ) {
428 if levels.is_empty() {
429 return;
430 }
431
432 let mut level_index = 0usize;
433 let mut run_level = levels[0];
434
435 let mut previous_properties: Option<Rc<dyn TextRunProperties>> = None;
436 let mut current_run: Option<&TextCharacters> = None;
437 let mut run_text: ReadOnlyMemory<u16> = ReadOnlyMemory::empty();
438
439 for text_run in text_characters {
440 let mut j = 0usize;
441 current_run = text_run.downcast_ref::<TextCharacters>();
442
443 let Some(current_run) = current_run else {
444 processed_runs.push(text_run.clone());
445
446 level_index += text_run.length().max(0) as usize;
447
448 continue;
449 };
450
451 run_text = current_run.text();
452
453 while j < run_text.len() {
454 let (_, count) = Codepoint::read_at(run_text.span(), j);
455
456 if level_index + 1 == levels.len() {
457 break;
458 }
459
460 level_index += 1;
461 j += count;
462
463 if j == run_text.len() {
464 current_run.get_shapeable_characters(
465 run_text.slice(0, j),
466 run_level,
467 font_manager,
468 &mut previous_properties,
469 processed_runs,
470 );
471
472 run_level = levels[level_index];
473
474 continue;
475 }
476
477 if levels[level_index] == run_level {
478 continue;
479 }
480
481 current_run.get_shapeable_characters(
483 run_text.slice(0, j),
484 run_level,
485 font_manager,
486 &mut previous_properties,
487 processed_runs,
488 );
489
490 run_text = run_text.slice_from(j);
491
492 j = 0;
493
494 run_level = levels[level_index];
496 }
497 }
498
499 let Some(current_run) = current_run else {
500 return;
501 };
502
503 if run_text.is_empty() {
504 return;
505 }
506
507 current_run.get_shapeable_characters(run_text, run_level, font_manager, &mut previous_properties, processed_runs);
508 }
509
510 fn fetch_text_runs(
515 text_source: &dyn ITextSource,
516 first_text_source_index: i32,
517 object_pool: &FormattingObjectPool,
518 ) -> (RentedList<Rc<dyn TextRun>>, Option<Rc<dyn TextRun>>, i32) {
519 let mut text_source_length = 0;
520
521 let mut end_of_line = None;
522
523 let mut text_runs = object_pool.text_run_lists.rent();
524
525 let mut text_run_enumerator = TextRunEnumerator::new(text_source, first_text_source_index);
526
527 while let Some(text_run) = text_run_enumerator.move_next() {
528 if let Some(text_end_of_line) = text_run.as_text_end_of_line() {
529 text_source_length += text_end_of_line.length();
530
531 end_of_line = Some(text_run.clone());
532
533 text_runs.push(text_run);
534
535 break;
536 }
537
538 if let Some(text_characters) = text_run.downcast_ref::<TextCharacters>() {
539 if let Some(run_line_break) = Self::try_get_line_break(text_characters) {
540 let split_result = TextCharacters::new(
541 text_characters.text().slice(0, run_line_break.position_wrap()),
542 text_characters.run_properties().clone(),
543 );
544
545 text_runs.push(Rc::new(split_result));
546
547 text_source_length += run_line_break.position_wrap() as i32;
548
549 return (text_runs, end_of_line, text_source_length);
550 }
551 }
552
553 text_source_length += text_run.length();
554
555 text_runs.push(text_run);
556 }
557
558 (text_runs, end_of_line, text_source_length)
559 }
560
561 fn try_get_line_break(text_run: &dyn TextRun) -> Option<LineBreak> {
562 let text = text_run.text_span();
563
564 if text.is_empty() {
565 return None;
566 }
567
568 let mut line_break_enumerator = LineBreakEnumerator::new(text);
569
570 while let Some(line_break) = line_break_enumerator.move_next() {
571 if !line_break.required() {
572 continue;
573 }
574
575 return Some(line_break);
576 }
577
578 None
579 }
580
581 fn measure_length(text_runs: &[Rc<dyn TextRun>], paragraph_width: f64) -> i32 {
582 let mut measured_length = 0;
583 let mut current_width = 0.0;
584
585 for (run_index, current_run) in text_runs.iter().enumerate() {
586 if let Some(shaped_text_characters) = current_run.downcast_ref::<ShapedTextRun>() {
587 let buffer = shaped_text_characters.shaped_buffer();
594
595 if buffer.length() == 0 {
596 continue;
597 }
598
599 let remaining = paragraph_width - current_width;
600 let buffer_width = buffer.total_glyph_advance();
601
602 if !MathUtilities::greater_than(buffer_width, remaining) {
603 current_width += buffer_width;
605 measured_length += current_run.length();
606 continue;
607 }
608
609 let mut run_length = buffer.find_leading_char_count_within_width(remaining);
611
612 if run_length == 0 && measured_length == 0 {
616 run_length = buffer.first_cluster_char_length();
617 }
618
619 measured_length += run_length;
620
621 if run_index < text_runs.len() - 1 && run_length == current_run.length() {
622 if let Some(end_of_line) = text_runs[run_index + 1].as_text_end_of_line() {
623 measured_length += end_of_line.length();
624 }
625 }
626
627 return measured_length;
628 } else if let Some(drawable_text_run) = current_run.as_drawable() {
629 if MathUtilities::greater_than(current_width + drawable_text_run.size().width, paragraph_width) {
630 return measured_length;
631 }
632
633 measured_length += current_run.length();
634 current_width += drawable_text_run.size().width;
635 } else {
636 measured_length += current_run.length();
637 }
638 }
639
640 measured_length
641 }
642
643 pub fn create_empty_text_line(
645 first_text_source_index: i32,
646 paragraph_width: f64,
647 paragraph_properties: &Rc<dyn TextParagraphProperties>,
648 ) -> Rc<TextLineImpl> {
649 let flow_direction = paragraph_properties.flow_direction();
650 let properties = paragraph_properties.default_text_run_properties();
651 let glyph_typeface = properties.cached_glyph_typeface();
652 let glyph = glyph_typeface.character_to_glyph_map().get_glyph(b' ' as i32);
653
654 let shaped_buffer = ShapedBuffer::new(
655 EMPTY.with(|empty| empty.clone()),
656 1,
657 glyph_typeface,
658 properties.font_rendering_em_size(),
659 flow_direction as i8,
660 );
661 shaped_buffer.set(0, GlyphInfo::new(glyph, first_text_source_index, 0.0));
662
663 let text_runs: Vec<Rc<dyn TextRun>> = vec![Rc::new(ShapedTextRun::new(shaped_buffer, properties.clone()))];
664
665 let line = TextLineImpl::new(
666 text_runs,
667 first_text_source_index,
668 0,
669 paragraph_width,
670 paragraph_properties.clone(),
671 flow_direction,
672 None,
673 false,
674 );
675
676 line.finalize_line();
677
678 line
679 }
680
681 #[allow(clippy::too_many_arguments)]
691 fn perform_text_wrapping(
692 text_runs: &mut Vec<Rc<dyn TextRun>>,
693 can_reuse_text_run_list: bool,
694 first_text_source_index: i32,
695 paragraph_width: f64,
696 paragraph_properties: &Rc<dyn TextParagraphProperties>,
697 resolved_flow_direction: FlowDirection,
698 current_line_break: Option<&Rc<TextLineBreak>>,
699 object_pool: &FormattingObjectPool,
700 ) -> Rc<TextLineImpl> {
701 if text_runs.is_empty() {
702 return Self::create_empty_text_line(first_text_source_index, paragraph_width, paragraph_properties);
703 }
704
705 let measured_length = Self::find_wrap_length(text_runs, paragraph_width, &**paragraph_properties);
706
707 let (split, split_length) = Self::split_text_runs_with_length(text_runs, measured_length, object_pool);
708
709 let (mut pre_split_runs, mut post_split_runs) = split.deconstruct();
710
711 let has_post_split_runs = post_split_runs.as_ref().is_some_and(|runs| !runs.is_empty());
712
713 let text_line_break = match &mut post_split_runs {
714 Some(post_split_runs) if has_post_split_runs => {
715 let post_split_count = post_split_runs.len();
716
717 let mut remaining_runs = if can_reuse_text_run_list {
720 let mut remaining_runs = std::mem::take(text_runs);
721 remaining_runs.clear();
722 remaining_runs.reserve(post_split_count);
724 remaining_runs
725 } else {
726 Vec::with_capacity(post_split_count)
727 };
728
729 remaining_runs.append(post_split_runs);
731
732 Some(Rc::new(WrappingTextLineBreak::new(None, resolved_flow_direction, remaining_runs)))
733 }
734 _ => current_line_break.and_then(|line_break| line_break.text_end_of_line()).map(|text_end_of_line| {
735 Rc::new(TextLineBreak::new(Some(text_end_of_line.clone()), resolved_flow_direction, false))
736 }),
737 };
738
739 let text_line = match &mut pre_split_runs {
740 None => Self::create_empty_text_line(first_text_source_index, paragraph_width, paragraph_properties),
741 Some(pre_split_runs) => {
742 if has_post_split_runs {
743 Self::reset_trailing_whitespace_bidi_levels(
744 pre_split_runs,
745 paragraph_properties.flow_direction(),
746 object_pool,
747 );
748 }
749
750 let remaining_text_runs: Vec<Rc<dyn TextRun>> = pre_split_runs.drain(..).collect();
755
756 let text_line = TextLineImpl::new(
757 remaining_text_runs,
758 first_text_source_index,
759 split_length,
760 paragraph_width,
761 paragraph_properties.clone(),
762 resolved_flow_direction,
763 text_line_break,
764 false,
765 );
766
767 text_line.finalize_line();
768
769 text_line
770 }
771 };
772
773 object_pool.text_run_lists.return_optional(pre_split_runs);
774 object_pool.text_run_lists.return_optional(post_split_runs);
775
776 text_line
777 }
778
779 fn find_wrap_length(
782 text_runs: &[Rc<dyn TextRun>],
783 paragraph_width: f64,
784 paragraph_properties: &dyn TextParagraphProperties,
785 ) -> i32 {
786 let mut measured_length = Self::measure_length(text_runs, paragraph_width);
787
788 if measured_length == 0 {
789 if paragraph_properties.text_wrapping() == TextWrapping::NoWrap {
790 for text_run in text_runs {
791 measured_length += text_run.length();
792 }
793 } else {
794 let first_run = &text_runs[0];
795
796 if first_run.is::<ShapedTextRun>() {
797 let mut grapheme_enumerator = GraphemeEnumerator::new(first_run.text_span());
798
799 measured_length = match grapheme_enumerator.move_next() {
800 Some(grapheme) => grapheme.length() as i32,
801 None => 1,
802 };
803 } else {
804 measured_length = first_run.length();
805 }
806 }
807 }
808
809 let mut current_length = 0;
810
811 let mut last_wrap_position = 0;
812
813 let mut current_position = 0;
814
815 let wrapping_mode = paragraph_properties.text_wrapping();
816 let run_count = text_runs.len();
817
818 let mut index = 0;
819
820 while index < run_count {
821 let mut break_found = false;
822
823 let mut current_run = &text_runs[index];
824 let mut current_run_length = current_run.length();
825
826 if current_run.is::<ShapedTextRun>() {
827 let mut line_breaker = LineBreakEnumerator::new(current_run.text_span());
828
829 while let Some(mut line_break) = line_breaker.move_next() {
830 if line_break.required() && current_length + line_break.position_measure() as i32 <= measured_length {
831 break_found = true;
833
834 current_position = current_length + line_break.position_wrap() as i32;
835
836 break;
837 }
838
839 if current_length + line_break.position_measure() as i32 > measured_length {
840 if wrapping_mode == TextWrapping::WrapWithOverflow {
841 if last_wrap_position > 0 {
842 current_position = last_wrap_position;
843
844 break_found = true;
845
846 break;
847 }
848
849 if index < run_count - 1 {
851 if line_break.position_wrap() as i32 != current_run_length {
852 break_found = true;
854
855 current_position = current_length + line_break.position_wrap() as i32;
856
857 break;
858 }
859
860 while let Some(next_line_break) = line_breaker.move_next() {
861 line_break = next_line_break;
862
863 current_position += line_break.position_wrap() as i32;
864
865 if line_break.position_wrap() as i32 != current_run_length {
866 break;
867 }
868
869 index += 1;
870
871 if index >= run_count {
872 break;
873 }
874
875 current_run = &text_runs[index];
876 current_run_length = current_run.length();
877
878 line_breaker = LineBreakEnumerator::new(current_run.text_span());
879 }
880 } else {
881 current_position = current_length + line_break.position_wrap() as i32;
882 }
883
884 if current_position == 0 && measured_length > 0 {
885 current_position = measured_length;
886 }
887
888 break_found = true;
889
890 break;
891 }
892
893 current_position = if last_wrap_position == 0 { measured_length } else { last_wrap_position };
895
896 break_found = true;
897
898 break;
899 }
900
901 if line_break.position_measure() != line_break.position_wrap()
902 || line_break.position_wrap() as i32 != current_run_length
903 {
904 last_wrap_position = current_length + line_break.position_wrap() as i32;
905 }
906 }
907 }
908
909 if !break_found {
910 current_length += current_run_length;
911
912 index += 1;
913
914 continue;
915 }
916
917 measured_length = current_position;
918
919 break;
920 }
921
922 measured_length
923 }
924
925 fn reset_trailing_whitespace_bidi_levels(
926 line_text_runs: &mut Vec<Rc<dyn TextRun>>,
927 paragraph_flow_direction: FlowDirection,
928 object_pool: &FormattingObjectPool,
929 ) {
930 if line_text_runs.is_empty() {
931 return;
932 }
933
934 let last_text_run_index = line_text_runs.len() - 1;
935
936 let last_text_run = line_text_runs[last_text_run_index].clone();
937
938 let Some(shaped_text) = last_text_run.downcast_ref::<ShapedTextRun>() else {
939 return;
940 };
941
942 let paragraph_embedding_level = paragraph_flow_direction as i8;
943
944 if shaped_text.bidi_level() == paragraph_embedding_level {
945 return;
946 }
947
948 let trailing_whitespace_length = shaped_text.glyph_run().metrics().trailing_whitespace_length;
949
950 if trailing_whitespace_length == 0 {
951 return;
952 }
953
954 let split_index = shaped_text.length() - trailing_whitespace_length;
955
956 let (text_runs, trailing_whitespace_runs) =
957 Self::split_text_runs(std::slice::from_ref(&last_text_run), split_index, object_pool).deconstruct();
958
959 let trailing_whitespace_runs = trailing_whitespace_runs.map(|mut trailing_whitespace_runs| {
960 for run in trailing_whitespace_runs.iter_mut() {
961 let replacement = run.downcast_ref::<ShapedTextRun>().and_then(|shaped_text_run| {
962 let new_buffer = shaped_text_run.shaped_buffer().with_bidi_level(paragraph_embedding_level);
963
964 if Rc::ptr_eq(&new_buffer, shaped_text_run.shaped_buffer()) {
965 return None;
966 }
967
968 let replacement: Rc<dyn TextRun> =
969 Rc::new(ShapedTextRun::new(new_buffer, shaped_text_run.run_properties().clone()));
970
971 shaped_text_run.dispose();
972
973 Some(replacement)
974 });
975
976 if let Some(replacement) = replacement {
977 *run = replacement;
978 }
979 }
980
981 line_text_runs.remove(last_text_run_index);
982
983 if let Some(text_runs) = &text_runs {
984 line_text_runs.extend(text_runs.iter().cloned());
985 }
986
987 line_text_runs.extend(trailing_whitespace_runs.iter().cloned());
988
989 trailing_whitespace_runs
990 });
991
992 object_pool.text_run_lists.return_optional(text_runs);
993 object_pool.text_run_lists.return_optional(trailing_whitespace_runs);
994 }
995}
996
997impl TextFormatter for TextFormatterImpl {
998 fn format_line(
999 &self,
1000 text_source: &dyn ITextSource,
1001 first_text_source_index: i32,
1002 paragraph_width: f64,
1003 paragraph_properties: &Rc<dyn TextParagraphProperties>,
1004 previous_line_break: Option<&Rc<TextLineBreak>>,
1005 ) -> Option<Rc<dyn TextLine>> {
1006 self.format_line_with_cache(
1007 text_source,
1008 first_text_source_index,
1009 paragraph_width,
1010 paragraph_properties,
1011 previous_line_break,
1012 None,
1013 )
1014 }
1015
1016 fn format_line_with_cache(
1017 &self,
1018 text_source: &dyn ITextSource,
1019 first_text_source_index: i32,
1020 paragraph_width: f64,
1021 paragraph_properties: &Rc<dyn TextParagraphProperties>,
1022 previous_line_break: Option<&Rc<TextLineBreak>>,
1023 text_run_cache: Option<&TextRunCache>,
1024 ) -> Option<Rc<dyn TextLine>> {
1025 crate::perf_count!(TextLinesFormatted);
1026 let object_pool = FormattingObjectPool::instance();
1027 let font_manager = FontManager::current();
1028
1029 if let Some(previous_line_break) = previous_line_break {
1031 if WrappingTextLineBreak::is_wrapping(previous_line_break) {
1032 if let Some(mut remaining_runs) = WrappingTextLineBreak::acquire_remaining_runs(previous_line_break) {
1033 if paragraph_properties.text_wrapping() != TextWrapping::NoWrap {
1034 return Some(Self::perform_text_wrapping(
1035 &mut remaining_runs,
1036 true,
1037 first_text_source_index,
1038 paragraph_width,
1039 paragraph_properties,
1040 previous_line_break.flow_direction(),
1041 Some(previous_line_break),
1042 object_pool,
1043 ));
1044 }
1045 }
1046 }
1047 }
1048
1049 if let Some(text_run_cache) = text_run_cache {
1051 if let Some(cached) = text_run_cache.try_get_shaped_runs(first_text_source_index) {
1052 crate::perf_count!(TextRunCacheHits);
1053 return Some(Self::format_line_from_cache(
1054 &cached,
1055 first_text_source_index,
1056 paragraph_width,
1057 paragraph_properties,
1058 object_pool,
1059 ));
1060 }
1061 }
1062
1063 let (fetched_runs, text_end_of_line, text_source_length) =
1064 Self::fetch_text_runs(text_source, first_text_source_index, object_pool);
1065
1066 if fetched_runs.is_empty() {
1067 object_pool.text_run_lists.return_list(fetched_runs);
1068
1069 return None;
1070 }
1071
1072 let (mut shaped_text_runs, resolved_flow_direction) =
1073 Self::shape_text_runs(&fetched_runs, &**paragraph_properties, object_pool, &font_manager);
1074
1075 let next_line_break = text_end_of_line
1076 .as_ref()
1077 .map(|text_end_of_line| Rc::new(TextLineBreak::new(Some(text_end_of_line.clone()), resolved_flow_direction, false)));
1078
1079 if let Some(text_run_cache) = text_run_cache {
1082 crate::perf_count!(TextRunCacheMisses);
1083 text_run_cache.add(
1084 first_text_source_index,
1085 CachedShapingResult::new(
1086 Rc::from(&shaped_text_runs[..]),
1087 resolved_flow_direction,
1088 text_end_of_line,
1089 text_source_length,
1090 ),
1091 );
1092 }
1093
1094 let text_line = match paragraph_properties.text_wrapping() {
1095 TextWrapping::NoWrap => {
1096 let line_runs: Vec<Rc<dyn TextRun>> = shaped_text_runs.drain(..).collect();
1098
1099 let text_line = TextLineImpl::new(
1100 line_runs,
1101 first_text_source_index,
1102 text_source_length,
1103 paragraph_width,
1104 paragraph_properties.clone(),
1105 resolved_flow_direction,
1106 next_line_break,
1107 false,
1108 );
1109
1110 text_line.finalize_line();
1111
1112 text_line
1113 }
1114 TextWrapping::WrapWithOverflow | TextWrapping::Wrap => Self::perform_text_wrapping(
1115 &mut shaped_text_runs,
1116 false,
1117 first_text_source_index,
1118 paragraph_width,
1119 paragraph_properties,
1120 resolved_flow_direction,
1121 next_line_break.as_ref(),
1122 object_pool,
1123 ),
1124 };
1125
1126 object_pool.text_run_lists.return_list(shaped_text_runs);
1127 object_pool.text_run_lists.return_list(fetched_runs);
1128
1129 Some(text_line)
1130 }
1131}
1132
1133struct TextRunEnumerator<'a> {
1134 text_source: &'a dyn ITextSource,
1135 pos: i32,
1136}
1137
1138impl<'a> TextRunEnumerator<'a> {
1139 fn new(text_source: &'a dyn ITextSource, first_text_source_index: i32) -> Self {
1140 Self { text_source, pos: first_text_source_index }
1141 }
1142
1143 fn move_next(&mut self) -> Option<Rc<dyn TextRun>> {
1144 let current = self.text_source.get_text_run(self.pos)?;
1145
1146 if current.length() == 0 {
1147 return None;
1148 }
1149
1150 self.pos += current.length();
1151
1152 Some(current)
1153 }
1154}