1use std::cell::{Cell, RefCell};
2use std::rc::Rc;
3
4use crate::media::text_formatting::{
5 GenericTextParagraphProperties, GenericTextRunProperties, ITextDrawingSink, ITextSource, TextCharacters,
6 TextEndOfParagraph, TextFormatter, TextLine, TextLineBreak, TextParagraphProperties, TextRun, TextRunProperties,
7};
8use crate::media::{
9 BaselineAlignment, Drawing, DrawingGroup, FillRule, FlowDirection, FontFamily, FontFeatureCollection,
10 FontStretch, FontStyle, FontWeight, Geometry, GeometryCombineMode, GeometryDrawing, GeometryGroup,
11 GlyphRunDrawing, IBrush, LineGeometry, RectangleGeometry, TextAlignment, TextCollapsingCreateInfo,
12 TextDecorationCollection, TextTrimming, TextWrapping, Transform, TranslateTransform, Typeface,
13};
14use crate::utilities::span::{SpanPosition, SpanRider, SpanVector};
15use crate::utilities::{CultureInfo, ReadOnlyMemory};
16use crate::{Point, Rect, Ref};
17
18pub struct FormattedText {
23 text: ReadOnlyMemory<u16>,
25 format_runs: RefCell<SpanVector<Rc<dyn TextRunProperties>>>,
26 latest_position: Cell<SpanPosition>,
27
28 default_para_props: Rc<GenericTextParagraphProperties>,
29
30 max_text_width: Cell<f64>,
31 max_text_widths: RefCell<Option<Vec<f64>>>,
32 max_text_height: Cell<f64>,
33 max_line_count: Cell<i32>,
34 trimming: RefCell<Rc<dyn TextTrimming>>,
35
36 metrics: Cell<Option<CachedMetrics>>,
38}
39
40impl FormattedText {
41 pub const DEFAULT_REAL_TO_IDEAL: f64 = 28800.0 / 96.0;
42 pub const DEFAULT_IDEAL_TO_REAL: f64 = 1.0 / Self::DEFAULT_REAL_TO_IDEAL;
43 pub const IDEAL_INFINITE_WIDTH: i32 = 0x3FFFFFFE;
44 pub const REAL_INFINITE_WIDTH: f64 = Self::IDEAL_INFINITE_WIDTH as f64 * Self::DEFAULT_IDEAL_TO_REAL;
45
46 pub const GREATEST_MULTIPLIER_OF_EM: f64 = 100.0;
47
48 const MAX_FONT_EM_SIZE: f64 = Self::REAL_INFINITE_WIDTH / Self::GREATEST_MULTIPLIER_OF_EM;
49
50 pub fn new(
61 text_to_format: &str,
62 culture: CultureInfo,
63 flow_direction: FlowDirection,
64 typeface: Typeface,
65 em_size: f64,
66 foreground: Option<Rc<dyn IBrush>>,
67 ) -> Self {
68 Self::validate_font_size(em_size);
69
70 let text = ReadOnlyMemory::<u16>::from_str(text_to_format);
71
72 let run_props: Rc<dyn TextRunProperties> = Rc::new(GenericTextRunProperties::with_all(
73 typeface,
74 em_size,
75 None, foreground,
77 None, BaselineAlignment::Baseline,
79 Some(culture),
80 None,
81 ));
82
83 let mut format_runs = SpanVector::new(None);
84
85 let latest_position =
86 format_runs.set_value_at(0, text.len() as i32, run_props.clone(), SpanPosition::default());
87
88 let default_para_props = Rc::new(GenericTextParagraphProperties::with_all(
89 flow_direction,
90 TextAlignment::Left,
91 false,
92 false,
93 run_props,
94 TextWrapping::WrapWithOverflow,
95 0.0, 0.0, 0.0,
98 ));
99
100 let formatted_text = Self {
101 text,
102 format_runs: RefCell::new(format_runs),
103 latest_position: Cell::new(latest_position),
104 default_para_props,
105 max_text_width: Cell::new(f64::INFINITY),
106 max_text_widths: RefCell::new(None),
107 max_text_height: Cell::new(f64::INFINITY),
108 max_line_count: Cell::new(i32::MAX),
109 trimming: RefCell::new(<dyn TextTrimming>::word_ellipsis()),
110 metrics: Cell::new(None),
111 };
112
113 formatted_text.invalidate_metrics();
114
115 formatted_text
116 }
117
118 fn validate_font_size(em_size: f64) {
119 if em_size <= 0.0 {
120 panic!("em_size: The parameter value must be greater than zero.");
121 }
122
123 if em_size > Self::MAX_FONT_EM_SIZE {
124 panic!("em_size: The parameter value cannot be greater than '{}'", Self::MAX_FONT_EM_SIZE);
125 }
126
127 if em_size.is_nan() {
128 panic!("em_size: The parameter value must be a number.");
129 }
130 }
131
132 fn text_length(&self) -> i32 {
133 self.text.len() as i32
134 }
135
136 fn validate_range(&self, start_index: i32, count: i32) -> i32 {
137 if start_index < 0 || start_index > self.text_length() {
138 panic!("start_index is out of range.");
139 }
140
141 let limit = start_index.wrapping_add(count);
142
143 if count < 0 || limit < start_index || limit > self.text_length() {
144 panic!("count is out of range.");
145 }
146
147 limit
148 }
149
150 fn invalidate_metrics(&self) {
151 self.metrics.set(None);
152 }
153
154 fn set_run_properties(
159 &self,
160 start_index: i32,
161 count: i32,
162 invalidate_metrics: bool,
163 create_new_props: impl Fn(&dyn TextRunProperties) -> Option<GenericTextRunProperties>,
164 ) {
165 let limit = self.validate_range(start_index, count);
166
167 let mut i = start_index;
168
169 while i < limit {
170 let (rider_length, current_position, span_position, run_props) = {
171 let format_runs = self.format_runs.borrow();
172 let format_rider = SpanRider::new(&format_runs, self.latest_position.get(), i);
173
174 (
175 format_rider.length(),
176 format_rider.current_position(),
177 format_rider.span_position(),
178 format_rider.current_element().cloned(),
179 )
180 };
181
182 i = limit.min(i.saturating_add(rider_length));
183
184 let Some(run_props) = run_props else {
186 panic!("run_props can not be null.");
187 };
188
189 let Some(new_props) = create_new_props(&*run_props) else {
190 continue;
191 };
192
193 let new_props: Rc<dyn TextRunProperties> = Rc::new(new_props);
194
195 let latest_position = self.format_runs.borrow_mut().set_value_at(
196 current_position,
197 i - current_position,
198 new_props,
199 span_position,
200 );
201
202 self.latest_position.set(latest_position);
203
204 if invalidate_metrics {
205 self.invalidate_metrics();
206 }
207 }
208 }
209
210 pub fn set_foreground_brush(&self, foreground_brush: Rc<dyn IBrush>) {
214 self.set_foreground_brush_range(Some(foreground_brush), 0, self.text_length());
215 }
216
217 pub fn set_foreground_brush_range(&self, foreground_brush: Option<Rc<dyn IBrush>>, start_index: i32, count: i32) {
223 self.set_run_properties(start_index, count, false, |run_props| {
224 let is_same = match (run_props.foreground_brush(), &foreground_brush) {
225 (None, None) => true,
226 (Some(current), Some(new)) => Rc::ptr_eq(current, new),
227 _ => false,
228 };
229
230 if is_same {
231 return None;
232 }
233
234 Some(GenericTextRunProperties::with_all(
235 run_props.typeface().clone(),
236 run_props.font_rendering_em_size(),
237 run_props.text_decorations().cloned(),
238 foreground_brush.clone(),
239 run_props.background_brush().cloned(),
240 run_props.baseline_alignment(),
241 run_props.culture_info().cloned(),
242 run_props.font_features().cloned(),
243 ))
244 });
245 }
246
247 pub fn set_font_features(&self, font_features: Option<FontFeatureCollection>) {
251 self.set_font_features_range(font_features, 0, self.text_length());
252 }
253
254 pub fn set_font_features_range(&self, font_features: Option<FontFeatureCollection>, start_index: i32, count: i32) {
260 self.set_run_properties(start_index, count, false, |run_props| {
261 let is_same = match (&font_features, run_props.font_features()) {
262 (None, None) => true,
263 (Some(new), Some(current)) => new.to_vec() == current.to_vec(),
264 _ => false,
265 };
266
267 if is_same {
268 return None;
269 }
270
271 Some(GenericTextRunProperties::with_all(
272 run_props.typeface().clone(),
273 run_props.font_rendering_em_size(),
274 run_props.text_decorations().cloned(),
275 run_props.foreground_brush().cloned(),
276 run_props.background_brush().cloned(),
277 run_props.baseline_alignment(),
278 run_props.culture_info().cloned(),
279 font_features.clone(),
280 ))
281 });
282 }
283
284 pub fn set_font_family_name(&self, font_family: &str) {
288 self.set_font_family_name_range(font_family, 0, self.text_length());
289 }
290
291 pub fn set_font_family_name_range(&self, font_family: &str, start_index: i32, count: i32) {
297 self.set_font_family_range(FontFamily::new(font_family), start_index, count);
298 }
299
300 pub fn set_font_family(&self, font_family: FontFamily) {
304 self.set_font_family_range(font_family, 0, self.text_length());
305 }
306
307 pub fn set_font_family_range(&self, font_family: FontFamily, start_index: i32, count: i32) {
313 self.set_run_properties(start_index, count, true, |run_props| {
314 let old_typeface = run_props.typeface();
315
316 if font_family == *old_typeface.font_family() {
317 return None;
318 }
319
320 Some(GenericTextRunProperties::with_all(
321 Typeface::with_style(
322 font_family.clone(),
323 old_typeface.style(),
324 old_typeface.weight(),
325 FontStretch::Normal,
326 ),
327 run_props.font_rendering_em_size(),
328 run_props.text_decorations().cloned(),
329 run_props.foreground_brush().cloned(),
330 run_props.background_brush().cloned(),
331 run_props.baseline_alignment(),
332 run_props.culture_info().cloned(),
333 run_props.font_features().cloned(),
334 ))
335 });
336 }
337
338 pub fn set_font_size(&self, em_size: f64) {
342 self.set_font_size_range(em_size, 0, self.text_length());
343 }
344
345 pub fn set_font_size_range(&self, em_size: f64, start_index: i32, count: i32) {
351 Self::validate_font_size(em_size);
352
353 self.set_run_properties(start_index, count, true, |run_props| {
354 if run_props.font_rendering_em_size() == em_size {
355 return None;
356 }
357
358 Some(GenericTextRunProperties::with_all(
359 run_props.typeface().clone(),
360 em_size,
361 run_props.text_decorations().cloned(),
362 run_props.foreground_brush().cloned(),
363 run_props.background_brush().cloned(),
364 run_props.baseline_alignment(),
365 run_props.culture_info().cloned(),
366 run_props.font_features().cloned(),
367 ))
368 });
369 }
370
371 pub fn set_culture(&self, culture: CultureInfo) {
375 self.set_culture_range(culture, 0, self.text_length());
376 }
377
378 pub fn set_culture_range(&self, culture: CultureInfo, start_index: i32, count: i32) {
384 self.set_run_properties(start_index, count, true, |run_props| {
385 if run_props.culture_info() == Some(&culture) {
386 return None;
387 }
388
389 Some(GenericTextRunProperties::with_all(
390 run_props.typeface().clone(),
391 run_props.font_rendering_em_size(),
392 run_props.text_decorations().cloned(),
393 run_props.foreground_brush().cloned(),
394 run_props.background_brush().cloned(),
395 run_props.baseline_alignment(),
396 Some(culture.clone()),
397 run_props.font_features().cloned(),
398 ))
399 });
400 }
401
402 pub fn set_font_weight(&self, weight: FontWeight) {
406 self.set_font_weight_range(weight, 0, self.text_length());
407 }
408
409 pub fn set_font_weight_range(&self, weight: FontWeight, start_index: i32, count: i32) {
415 self.set_run_properties(start_index, count, true, |run_props| {
416 let old_typeface = run_props.typeface();
417
418 if old_typeface.weight() == weight {
419 return None;
420 }
421
422 Some(GenericTextRunProperties::with_all(
423 Typeface::with_style(
424 old_typeface.font_family().clone(),
425 old_typeface.style(),
426 weight,
427 FontStretch::Normal,
428 ),
429 run_props.font_rendering_em_size(),
430 run_props.text_decorations().cloned(),
431 run_props.foreground_brush().cloned(),
432 run_props.background_brush().cloned(),
433 run_props.baseline_alignment(),
434 run_props.culture_info().cloned(),
435 run_props.font_features().cloned(),
436 ))
437 });
438 }
439
440 pub fn set_font_style(&self, style: FontStyle) {
444 self.set_font_style_range(style, 0, self.text_length());
445 }
446
447 pub fn set_font_style_range(&self, style: FontStyle, start_index: i32, count: i32) {
453 self.set_run_properties(start_index, count, true, |run_props| {
454 let old_typeface = run_props.typeface();
455
456 if old_typeface.style() == style {
457 return None;
458 }
459
460 Some(GenericTextRunProperties::with_all(
461 Typeface::with_style(
462 old_typeface.font_family().clone(),
463 style,
464 old_typeface.weight(),
465 FontStretch::Normal,
466 ),
467 run_props.font_rendering_em_size(),
468 run_props.text_decorations().cloned(),
469 run_props.foreground_brush().cloned(),
470 run_props.background_brush().cloned(),
471 run_props.baseline_alignment(),
472 run_props.culture_info().cloned(),
473 run_props.font_features().cloned(),
474 ))
475 });
476 }
477
478 pub fn set_font_typeface(&self, typeface: Typeface) {
482 self.set_font_typeface_range(typeface, 0, self.text_length());
483 }
484
485 pub fn set_font_typeface_range(&self, typeface: Typeface, start_index: i32, count: i32) {
491 self.set_run_properties(start_index, count, true, |run_props| {
492 if *run_props.typeface() == typeface {
493 return None;
494 }
495
496 Some(GenericTextRunProperties::with_all(
497 typeface.clone(),
498 run_props.font_rendering_em_size(),
499 run_props.text_decorations().cloned(),
500 run_props.foreground_brush().cloned(),
501 run_props.background_brush().cloned(),
502 run_props.baseline_alignment(),
503 run_props.culture_info().cloned(),
504 run_props.font_features().cloned(),
505 ))
506 });
507 }
508
509 pub fn set_text_decorations(&self, text_decorations: TextDecorationCollection) {
513 self.set_text_decorations_range(text_decorations, 0, self.text_length());
514 }
515
516 pub fn set_text_decorations_range(&self, text_decorations: TextDecorationCollection, start_index: i32, count: i32) {
522 self.set_run_properties(start_index, count, false, |run_props| {
523 if run_props.text_decorations() == Some(&text_decorations) {
524 return None;
525 }
526
527 Some(GenericTextRunProperties::with_all(
528 run_props.typeface().clone(),
529 run_props.font_rendering_em_size(),
530 Some(text_decorations.clone()),
531 run_props.foreground_brush().cloned(),
532 run_props.background_brush().cloned(),
533 run_props.baseline_alignment(),
534 run_props.culture_info().cloned(),
535 run_props.font_features().cloned(),
536 ))
537 });
538 }
539
540 fn get_enumerator(&self) -> LineEnumerator<'_> {
542 LineEnumerator::new(self)
543 }
544
545 fn advance_line_origin(&self, line_origin: &mut Point, current_line: &dyn TextLine) {
546 let height = current_line.height();
547
548 match self.default_para_props.flow_direction() {
550 FlowDirection::LeftToRight | FlowDirection::RightToLeft => {
551 *line_origin = line_origin.with_y(line_origin.y + height);
552 }
553 }
554 }
555
556 pub fn flow_direction(&self) -> FlowDirection {
558 self.default_para_props.flow_direction()
559 }
560
561 pub fn set_flow_direction(&self, value: FlowDirection) {
563 self.default_para_props.set_flow_direction(value);
564 self.invalidate_metrics();
565 }
566
567 pub fn text_alignment(&self) -> TextAlignment {
569 self.default_para_props.text_alignment()
570 }
571
572 pub fn set_text_alignment(&self, value: TextAlignment) {
574 self.default_para_props.set_text_alignment(value);
575 self.invalidate_metrics();
576 }
577
578 pub fn line_height(&self) -> f64 {
581 self.default_para_props.line_height()
582 }
583
584 pub fn set_line_height(&self, value: f64) {
589 if value < 0.0 {
590 panic!("value: Parameter must be greater than or equal to zero.");
591 }
592
593 self.default_para_props.set_line_height(value);
594
595 self.invalidate_metrics();
596 }
597
598 pub fn max_text_width(&self) -> f64 {
606 self.max_text_width.get()
607 }
608
609 pub fn set_max_text_width(&self, value: f64) {
611 if value < 0.0 {
612 panic!("value: Parameter must be greater than or equal to zero.");
613 }
614
615 self.max_text_width.set(value);
616
617 self.invalidate_metrics();
618 }
619
620 pub fn set_max_text_widths(&self, max_text_widths: &[f64]) {
629 if max_text_widths.is_empty() {
630 panic!("max_text_widths must not be empty.");
631 }
632
633 *self.max_text_widths.borrow_mut() = Some(max_text_widths.to_vec());
634
635 self.invalidate_metrics();
636 }
637
638 pub fn get_max_text_widths(&self) -> Vec<f64> {
643 self.max_text_widths.borrow().clone().unwrap_or_default()
644 }
645
646 pub fn max_text_height(&self) -> f64 {
651 self.max_text_height.get()
652 }
653
654 pub fn set_max_text_height(&self, value: f64) {
657 if value <= 0.0 {
658 panic!("value: 'MaxTextHeight' property value must be greater than zero.");
659 }
660
661 if value.is_nan() {
662 panic!("value: 'MaxTextHeight' property value cannot be NaN.");
663 }
664
665 self.max_text_height.set(value);
666
667 self.invalidate_metrics();
668 }
669
670 pub fn max_line_count(&self) -> i32 {
676 self.max_line_count.get()
677 }
678
679 pub fn set_max_line_count(&self, value: i32) {
682 if value <= 0 {
683 panic!("value: The parameter value must be greater than zero.");
684 }
685
686 self.max_line_count.set(value);
687
688 self.invalidate_metrics();
689 }
690
691 pub fn trimming(&self) -> Rc<dyn TextTrimming> {
697 self.trimming.borrow().clone()
698 }
699
700 pub fn set_trimming(&self, value: Rc<dyn TextTrimming>) {
702 *self.trimming.borrow_mut() = value;
703
704 self.default_para_props.set_text_wrapping(if self.is_trimming_none() {
705 TextWrapping::Wrap
706 } else {
707 TextWrapping::WrapWithOverflow
708 });
709
710 self.invalidate_metrics();
711 }
712
713 fn is_trimming_none(&self) -> bool {
715 Rc::ptr_eq(&self.trimming.borrow(), &<dyn TextTrimming>::none())
716 }
717
718 fn metrics(&self) -> CachedMetrics {
721 if let Some(metrics) = self.metrics.get() {
722 return metrics;
723 }
724
725 let metrics = self.draw_and_calculate_metrics(
726 None, Point::default(), false,
729 );
730
731 self.metrics.set(Some(metrics));
732
733 metrics
734 }
735
736 fn black_box_metrics(&self) -> CachedMetrics {
739 match self.metrics.get() {
740 Some(metrics) if !metrics.extent.is_nan() => metrics,
741 _ => {
742 let metrics = self.draw_and_calculate_metrics(
745 None, Point::default(), true, );
749
750 self.metrics.set(Some(metrics));
751
752 metrics
753 }
754 }
755 }
756
757 pub fn height(&self) -> f64 {
759 self.metrics().height
760 }
761
762 pub fn extent(&self) -> f64 {
765 self.black_box_metrics().extent
766 }
767
768 pub fn baseline(&self) -> f64 {
770 self.metrics().baseline
771 }
772
773 pub fn overhang_after(&self) -> f64 {
775 self.black_box_metrics().overhang_after
776 }
777
778 pub fn overhang_leading(&self) -> f64 {
780 self.black_box_metrics().overhang_leading
781 }
782
783 pub fn overhang_trailing(&self) -> f64 {
785 self.black_box_metrics().overhang_trailing
786 }
787
788 pub fn width(&self) -> f64 {
791 self.metrics().width
792 }
793
794 pub fn width_including_trailing_whitespace(&self) -> f64 {
797 self.metrics().width_including_trailing_whitespace
798 }
799
800 pub fn build_geometry(&self, origin: Point) -> Option<Ref<Geometry>> {
807 let mut accumulated_geometry: Option<Ref<GeometryGroup>> = None;
808 let mut line_origin = origin;
809
810 let drawing = DrawingGroup::new();
811
812 {
813 let mut ctx = drawing.open();
814
815 {
816 let mut enumerator = self.get_enumerator();
817
818 while enumerator.move_next() {
819 let Some(current_line) = enumerator.current() else {
820 continue;
821 };
822
823 current_line.draw(&mut ctx, line_origin);
824
825 self.advance_line_origin(&mut line_origin, &*current_line);
826 }
827
828 enumerator.dispose();
829 }
830
831 ctx.dispose();
832 }
833
834 let mut transform: Option<Ref<Transform>> = Some(TranslateTransform::with_offset(origin.x, origin.y).upcast());
835
836 Self::combine_geometry_recursive(&drawing.upcast(), &mut transform, &mut accumulated_geometry);
838
839 accumulated_geometry.map(|geometry| geometry.upcast())
840 }
841
842 fn combine_geometry_recursive(
843 drawing: &Ref<Drawing>,
844 transform: &mut Option<Ref<Transform>>,
845 accumulated_geometry: &mut Option<Ref<GeometryGroup>>,
846 ) {
847 fn accumulated(accumulated_geometry: &mut Option<Ref<GeometryGroup>>) -> &Ref<GeometryGroup> {
848 accumulated_geometry.get_or_insert_with(|| {
849 let group = GeometryGroup::new();
850 group.set_fill_rule(FillRule::NonZero);
851 group
852 })
853 }
854
855 if let Some(group) = drawing.cast::<DrawingGroup>() {
856 *transform = group.transform();
857
858 for child in group.children().iter() {
860 Self::combine_geometry_recursive(&child, transform, accumulated_geometry);
861 }
862 } else if let Some(glyph_run_drawing) = drawing.cast::<GlyphRunDrawing>() {
863 if let Some(glyph_run) = glyph_run_drawing.glyph_run() {
865 let glyph_run_geometry = glyph_run.build_geometry();
866
867 glyph_run_geometry.set_transform(transform.clone());
868
869 accumulated(accumulated_geometry).children().add(glyph_run_geometry);
870 }
871 } else if let Some(geometry_drawing) = drawing.cast::<GeometryDrawing>() {
872 if let Some(mut geometry) = geometry_drawing.geometry() {
874 geometry.set_transform(transform.clone());
875
876 if let Some(line_geometry) = geometry.cast::<LineGeometry>() {
877 let mut bounds = line_geometry.bounds();
882
883 let thickness = geometry_drawing.pen().map_or(0.0, |pen| pen.thickness());
884
885 if bounds.height == 0.0 {
886 bounds = bounds.with_height(thickness);
887 } else if bounds.width == 0.0 {
888 bounds = bounds.with_width(thickness);
889 }
890
891 geometry = RectangleGeometry::with_rect(bounds).upcast();
894 }
895
896 accumulated(accumulated_geometry).children().add(geometry);
897 }
898 }
899 }
900
901 pub fn build_highlight_geometry(&self, origin: Point) -> Option<Ref<Geometry>> {
907 self.build_highlight_geometry_range(origin, 0, self.text_length())
908 }
909
910 pub fn build_highlight_geometry_range(&self, origin: Point, start_index: i32, count: i32) -> Option<Ref<Geometry>> {
918 self.validate_range(start_index, count);
919
920 let mut accumulated_bounds: Option<Ref<Geometry>> = None;
921
922 {
923 let mut enumerator = self.get_enumerator();
924
925 let mut line_origin = origin;
926
927 while enumerator.move_next() {
928 let Some(current_line) = enumerator.current() else {
929 continue;
930 };
931
932 let x0 = enumerator.position().max(start_index);
933 let x1 = (enumerator.position() + enumerator.length()).min(start_index + count);
934
935 if x0 < x1 {
937 let highlight_bounds = current_line.get_text_bounds(x0, x1 - x0);
938
939 for bound in &highlight_bounds {
940 let mut rect = bound.rectangle();
941
942 if self.flow_direction() == FlowDirection::RightToLeft {
943 rect = rect.with_x(enumerator.current_paragraph_width() - rect.right());
952 }
953
954 rect = Rect::new(rect.x + line_origin.x, rect.y + line_origin.y, rect.width, rect.height);
955
956 let rectangle_geometry = RectangleGeometry::with_rect(rect);
957
958 accumulated_bounds = Some(match accumulated_bounds {
959 None => rectangle_geometry.upcast(),
960 Some(accumulated_bounds) => Geometry::combine(
961 accumulated_bounds,
962 &rectangle_geometry,
963 GeometryCombineMode::Union,
964 None::<Ref<Transform>>,
965 ),
966 });
967 }
968 }
969
970 self.advance_line_origin(&mut line_origin, &*current_line);
971 }
972
973 enumerator.dispose();
974 }
975
976 let accumulated_bounds = accumulated_bounds?;
977
978 let bounds = accumulated_bounds.platform_impl()?.bounds();
979
980 if bounds.width == 0.0 && bounds.height == 0.0 {
981 return None;
982 }
983
984 Some(accumulated_bounds)
985 }
986
987 pub(crate) fn draw(&self, drawing_context: &mut dyn ITextDrawingSink, origin: Point) {
989 let mut line_origin = origin;
990
991 if self.metrics.get().is_some_and(|metrics| !metrics.extent.is_nan()) {
992 let mut enumerator = self.get_enumerator();
993
994 while enumerator.move_next() {
995 let Some(current_line) = enumerator.current() else {
996 continue;
997 };
998
999 current_line.draw(drawing_context, line_origin);
1000
1001 self.advance_line_origin(&mut line_origin, &*current_line);
1002 }
1003
1004 enumerator.dispose();
1005 } else {
1006 let metrics = self.draw_and_calculate_metrics(Some(drawing_context), origin, true);
1010
1011 self.metrics.set(Some(metrics));
1012 }
1013 }
1014
1015 fn draw_and_calculate_metrics(
1016 &self,
1017 mut drawing_context: Option<&mut dyn ITextDrawingSink>,
1018 drawing_offset: Point,
1019 get_black_box_metrics: bool,
1020 ) -> CachedMetrics {
1021 let mut metrics = CachedMetrics::default();
1022
1023 if self.text.is_empty() {
1024 return metrics;
1025 }
1026
1027 let mut enumerator = self.get_enumerator();
1028
1029 let mut first = true;
1030
1031 let mut acc_black_box_left = f64::MAX;
1032 let mut acc_black_box_top = f64::MAX;
1033 let mut acc_black_box_right = f64::MIN;
1034 let mut acc_black_box_bottom = f64::MIN;
1035
1036 let mut origin = Point::new(0.0, 0.0);
1037
1038 let mut line_start_of_longest_line = f64::MAX;
1043
1044 while enumerator.move_next() {
1045 let Some(current_line) = enumerator.current() else {
1046 continue;
1047 };
1048
1049 if let Some(drawing_context) = drawing_context.as_deref_mut() {
1051 current_line
1052 .draw(drawing_context, Point::new(origin.x + drawing_offset.x, origin.y + drawing_offset.y));
1053 }
1054
1055 if get_black_box_metrics {
1056 let black_box_left = origin.x + current_line.start() + current_line.overhang_leading();
1057 let black_box_right =
1058 origin.x + current_line.start() + current_line.width() - current_line.overhang_trailing();
1059 let black_box_bottom = origin.y + current_line.height() + current_line.overhang_after();
1060 let black_box_top = black_box_bottom - current_line.extent();
1061
1062 acc_black_box_left = acc_black_box_left.min(black_box_left);
1063 acc_black_box_right = acc_black_box_right.max(black_box_right);
1064 acc_black_box_bottom = acc_black_box_bottom.max(black_box_bottom);
1065 acc_black_box_top = acc_black_box_top.min(black_box_top);
1066
1067 metrics.overhang_after = current_line.overhang_after();
1068 }
1069
1070 metrics.height += current_line.height();
1071 metrics.width = metrics.width.max(current_line.width());
1072 metrics.width_including_trailing_whitespace = metrics
1073 .width_including_trailing_whitespace
1074 .max(current_line.width_including_trailing_whitespace());
1075 line_start_of_longest_line = line_start_of_longest_line.min(current_line.start());
1076
1077 if first {
1078 metrics.baseline = current_line.baseline();
1079 first = false;
1080 }
1081
1082 self.advance_line_origin(&mut origin, &*current_line);
1083 }
1084
1085 if get_black_box_metrics {
1086 metrics.extent = acc_black_box_bottom - acc_black_box_top;
1087 metrics.overhang_leading = acc_black_box_left - line_start_of_longest_line;
1088 metrics.overhang_trailing = metrics.width - (acc_black_box_right - line_start_of_longest_line);
1089 } else {
1090 metrics.extent = f64::NAN;
1092 }
1093
1094 enumerator.dispose();
1095
1096 metrics
1097 }
1098}
1099
1100struct LineEnumerator<'a> {
1102 line_count: i32,
1103 total_height: f64,
1104 next_line: Option<Rc<dyn TextLine>>,
1105 formatter: Rc<dyn TextFormatter>,
1106 that: &'a FormattedText,
1107 text_source: TextSourceImplementation<'a>,
1108 para_props: Rc<dyn TextParagraphProperties>,
1109
1110 previous_height: f64,
1112
1113 previous_line_break: Option<Rc<TextLineBreak>>,
1115 position: i32,
1116 length: i32,
1117
1118 current: Option<Rc<dyn TextLine>>,
1119}
1120
1121impl<'a> LineEnumerator<'a> {
1122 fn new(text: &'a FormattedText) -> Self {
1123 Self {
1124 previous_height: 0.0,
1125 length: 0,
1126 previous_line_break: None,
1127 position: 0,
1128 line_count: 0,
1129 total_height: 0.0,
1130 current: None,
1131 next_line: None,
1132 formatter: <dyn TextFormatter>::current(),
1133 that: text,
1134 text_source: TextSourceImplementation::new(text),
1135 para_props: text.default_para_props.clone(),
1136 }
1137 }
1138
1139 fn dispose(&mut self) {
1140 self.current = None;
1141
1142 self.next_line = None;
1143 }
1144
1145 fn position(&self) -> i32 {
1146 self.position
1147 }
1148
1149 fn length(&self) -> i32 {
1150 self.length
1151 }
1152
1153 fn current(&self) -> Option<Rc<dyn TextLine>> {
1155 self.current.clone()
1156 }
1157
1158 fn current_paragraph_width(&self) -> f64 {
1160 self.max_line_length(self.line_count)
1161 }
1162
1163 fn max_line_length(&self, line: i32) -> f64 {
1164 match &*self.that.max_text_widths.borrow() {
1165 None => self.that.max_text_width.get(),
1166 Some(max_text_widths) => max_text_widths[(line as usize).min(max_text_widths.len() - 1)],
1167 }
1168 }
1169
1170 fn move_next(&mut self) -> bool {
1175 let that = self.that;
1176
1177 let current = match self.current.take() {
1178 None => {
1179 if that.text.is_empty() {
1181 return false;
1182 }
1183
1184 let current = self.format_line(
1185 self.position,
1186 self.max_line_length(self.line_count),
1187 None, );
1189
1190 let Some(current) = current else {
1191 return false;
1192 };
1193
1194 if self.total_height + current.height() > that.max_text_height.get() {
1196 return false;
1197 }
1198
1199 debug_assert!(self.next_line.is_none());
1200
1201 current
1202 }
1203 Some(previous) => {
1204 let Some(next_line) = self.next_line.take() else {
1207 self.current = Some(previous);
1208
1209 return false;
1210 };
1211
1212 self.total_height += self.previous_height;
1213 self.position += self.length;
1214 self.line_count += 1;
1215
1216 next_line
1217 }
1218 };
1219
1220 let mut current = Some(current);
1221
1222 let mut current_line_break = current.as_ref().and_then(|current| current.text_line_break());
1223
1224 if let Some(current_line) = current.clone() {
1225 debug_assert!(self.total_height + current_line.height() <= that.max_text_height.get());
1227
1228 if self.position + current_line.length() < that.text_length() {
1234 let mut next_line_fits = false;
1235
1236 if self.line_count + 1 >= that.max_line_count.get() {
1237 next_line_fits = false;
1238 } else {
1239 self.next_line = self.format_line(
1240 self.position + current_line.length(),
1241 self.max_line_length(self.line_count + 1),
1242 current_line_break.as_ref(),
1243 );
1244
1245 if let Some(next_line) = &self.next_line {
1246 next_line_fits = self.total_height + current_line.height() + next_line.height()
1247 <= that.max_text_height.get();
1248 }
1249 }
1250
1251 if !next_line_fits {
1252 self.next_line = None;
1253
1254 if !that.is_trimming_none() && !current_line.has_collapsed() {
1255 let current_wrap = that.default_para_props.text_wrapping();
1259
1260 that.default_para_props.set_text_wrapping(TextWrapping::NoWrap);
1261
1262 current = self.format_line(
1263 self.position,
1264 self.max_line_length(self.line_count),
1265 self.previous_line_break.as_ref(),
1266 );
1267
1268 if let Some(current) = ¤t {
1269 current_line_break = current.text_line_break();
1270 }
1271
1272 that.default_para_props.set_text_wrapping(current_wrap);
1273 }
1274 }
1275 }
1276 }
1277
1278 if let Some(current) = ¤t {
1279 self.previous_height = current.height();
1280
1281 self.length = current.length();
1282 }
1283
1284 self.current = current;
1285
1286 self.previous_line_break = current_line_break;
1287
1288 true
1289 }
1290
1291 fn format_line(
1293 &self,
1294 text_source_position: i32,
1295 max_line_length: f64,
1296 line_break: Option<&Rc<TextLineBreak>>,
1297 ) -> Option<Rc<dyn TextLine>> {
1298 let that = self.that;
1299
1300 let mut line = self.formatter.format_line(
1301 &self.text_source,
1302 text_source_position,
1303 max_line_length,
1304 &self.para_props,
1305 line_break,
1306 );
1307
1308 if let Some(overflowed_line) = &line {
1309 if !that.is_trimming_none() && overflowed_line.has_overflowed() && overflowed_line.length() > 0 {
1310 debug_assert!(
1314 that.text_length() > 0
1315 && text_source_position + overflowed_line.length() <= that.text_length() + 1
1316 );
1317
1318 let last_run_props = {
1319 let format_runs = that.format_runs.borrow();
1320
1321 let that_format_rider = SpanRider::new(
1322 &format_runs,
1323 that.latest_position.get(),
1324 (text_source_position + overflowed_line.length() - 1).min(that.text_length() - 1),
1325 );
1326
1327 that_format_rider.current_element().cloned().expect("the format runs cover the text")
1328 };
1329
1330 let collapsing_properties =
1331 that.trimming.borrow().create_collapsing_properties(&TextCollapsingCreateInfo::new(
1332 max_line_length,
1333 last_run_props,
1334 self.para_props.flow_direction(),
1335 ));
1336
1337 let collapsed_line = overflowed_line.clone().collapse(&[Some(collapsing_properties)]);
1338
1339 line = Some(collapsed_line);
1340 }
1341 }
1342
1343 line
1344 }
1345
1346 #[allow(dead_code)] fn reset(&mut self) {
1350 self.position = 0;
1351 self.line_count = 0;
1352 self.total_height = 0.0;
1353 self.current = None;
1354 self.next_line = None;
1355 }
1356}
1357
1358#[derive(Clone, Copy, Default)]
1359struct CachedMetrics {
1360 height: f64,
1362 baseline: f64,
1363
1364 width: f64,
1366 width_including_trailing_whitespace: f64,
1367
1368 extent: f64,
1370 overhang_after: f64,
1371
1372 overhang_leading: f64,
1374 overhang_trailing: f64,
1375}
1376
1377struct TextSourceImplementation<'a> {
1380 that: &'a FormattedText,
1381}
1382
1383impl<'a> TextSourceImplementation<'a> {
1384 fn new(text: &'a FormattedText) -> Self {
1385 Self { that: text }
1386 }
1387}
1388
1389impl ITextSource for TextSourceImplementation<'_> {
1390 fn get_text_run(&self, text_source_character_index: i32) -> Option<Rc<dyn TextRun>> {
1391 let that = self.that;
1392
1393 if text_source_character_index >= that.text_length() {
1394 return Some(Rc::new(TextEndOfParagraph::new()));
1395 }
1396
1397 let format_runs = that.format_runs.borrow();
1398
1399 let that_format_rider = SpanRider::new(&format_runs, that.latest_position.get(), text_source_character_index);
1400
1401 let text = that.text.slice(text_source_character_index as usize, that_format_rider.length() as usize);
1402
1403 let properties = that_format_rider.current_element().cloned().expect("the format runs cover the text");
1404
1405 Some(Rc::new(TextCharacters::new(text, properties)))
1406 }
1407}
1408
1409#[cfg(test)]
1410#[path = "formatted_text_tests.rs"]
1411mod tests;