skia_safe/modules/paragraph/
metrics.rs

1use crate::{paragraph::TextStyle, prelude::*, FontMetrics};
2use skia_bindings::{self as sb, skia_textlayout_LineMetrics, skia_textlayout_StyleMetrics};
3use std::{marker::PhantomData, ops::Range, ptr};
4
5#[repr(C)]
6#[derive(Clone, Debug)]
7pub struct StyleMetrics<'a> {
8    pub text_style: &'a TextStyle,
9
10    /// [`FontMetrics`] contains the following metrics:
11    ///
12    /// * Top                 distance to reserve above baseline
13    /// * Ascent              distance to reserve below baseline
14    /// * Descent             extent below baseline
15    /// * Bottom              extent below baseline
16    /// * Leading             distance to add between lines
17    /// * AvgCharWidth        average character width
18    /// * MaxCharWidth        maximum character width
19    /// * XMin                minimum x
20    /// * XMax                maximum x
21    /// * XHeight             height of lower-case 'x'
22    /// * CapHeight           height of an upper-case letter
23    /// * UnderlineThickness  underline thickness
24    /// * UnderlinePosition   underline position relative to baseline
25    /// * StrikeoutThickness  strikeout thickness
26    /// * StrikeoutPosition   strikeout position relative to baseline
27    pub font_metrics: FontMetrics,
28}
29
30native_transmutable!(
31    skia_textlayout_StyleMetrics,
32    StyleMetrics<'_>,
33    style_metrics_layout
34);
35
36impl<'a> StyleMetrics<'a> {
37    pub fn new(style: &'a TextStyle, metrics: impl Into<Option<FontMetrics>>) -> Self {
38        Self {
39            text_style: style,
40            font_metrics: metrics.into().unwrap_or_default(),
41        }
42    }
43}
44
45#[derive(Clone, Debug)]
46pub struct LineMetrics<'a> {
47    // The following fields are used in the layout process itself.
48    /// The index in the text buffer the line begins.
49    pub start_index: usize,
50    /// The index in the text buffer the line ends.
51    pub end_index: usize,
52    pub end_excluding_whitespaces: usize,
53    pub end_including_newline: usize,
54    pub hard_break: bool,
55
56    // The following fields are tracked after or during layout to provide to
57    // the user as well as for computing bounding boxes.
58    /// The final computed ascent and descent for the line. This can be impacted by
59    /// the strut, height, scaling, as well as outlying runs that are very tall.
60    ///
61    /// The top edge is `baseline - ascent` and the bottom edge is `baseline +
62    /// descent`. Ascent and descent are provided as positive numbers. Raw numbers
63    /// for specific runs of text can be obtained in run_metrics_map. These values
64    /// are the cumulative metrics for the entire line.
65    pub ascent: f64,
66    pub descent: f64,
67    pub unscaled_ascent: f64,
68    /// Total height of the paragraph including the current line.
69    ///
70    /// The height of the current line is `round(ascent + descent)`.
71    pub height: f64,
72    /// Width of the line.
73    pub width: f64,
74    /// The left edge of the line. The right edge can be obtained with `left +
75    /// width`
76    pub left: f64,
77    /// The y position of the baseline for this line from the top of the paragraph.
78    pub baseline: f64,
79    /// Zero indexed line number
80    pub line_number: usize,
81    /// Mapping between text index ranges and the FontMetrics associated with
82    /// them. The first run will be keyed under start_index. The metrics here
83    /// are before layout and are the base values we calculate from.
84    style_metrics: Vec<sb::IndexedStyleMetrics>,
85    pd: PhantomData<&'a StyleMetrics<'a>>,
86}
87
88impl<'a> LineMetrics<'a> {
89    // TODO: may support constructors (but what about the lifetime bounds?).
90
91    /// Returns the number of style metrics in the given index range.
92    pub fn get_style_metrics_count(&self, range: Range<usize>) -> usize {
93        let lower = self
94            .style_metrics
95            .partition_point(|ism| ism.index < range.start);
96        let upper = self
97            .style_metrics
98            .partition_point(|ism| ism.index < range.end);
99        upper - lower
100    }
101
102    /// Returns indices and references to style metrics in the given range.
103    pub fn get_style_metrics(&'a self, range: Range<usize>) -> Vec<(usize, &'a StyleMetrics<'a>)> {
104        let lower = self
105            .style_metrics
106            .partition_point(|ism| ism.index < range.start);
107        let upper = self
108            .style_metrics
109            .partition_point(|ism| ism.index < range.end);
110        self.style_metrics[lower..upper]
111            .iter()
112            .map(|ism| (ism.index, StyleMetrics::from_native_ref(&ism.metrics)))
113            .collect()
114    }
115
116    // We can't use a `std::map` in rust, it does not seem to be safe to move. So we copy it into a
117    // sorted Vec.
118    pub(crate) fn from_native_ref<'b>(lm: &skia_textlayout_LineMetrics) -> LineMetrics<'b> {
119        let sm_count = unsafe { sb::C_LineMetrics_styleMetricsCount(lm) };
120        let mut style_metrics = vec![
121            sb::IndexedStyleMetrics {
122                index: 0,
123                metrics: sb::skia_textlayout_StyleMetrics {
124                    text_style: ptr::null(),
125                    font_metrics: sb::SkFontMetrics {
126                        fFlags: 0,
127                        fTop: 0.0,
128                        fAscent: 0.0,
129                        fDescent: 0.0,
130                        fBottom: 0.0,
131                        fLeading: 0.0,
132                        fAvgCharWidth: 0.0,
133                        fMaxCharWidth: 0.0,
134                        fXMin: 0.0,
135                        fXMax: 0.0,
136                        fXHeight: 0.0,
137                        fCapHeight: 0.0,
138                        fUnderlineThickness: 0.0,
139                        fUnderlinePosition: 0.0,
140                        fStrikeoutThickness: 0.0,
141                        fStrikeoutPosition: 0.0
142                    }
143                }
144            };
145            sm_count
146        ];
147
148        unsafe { sb::C_LineMetrics_getAllStyleMetrics(lm, style_metrics.as_mut_ptr()) }
149
150        LineMetrics {
151            start_index: lm.fStartIndex,
152            end_index: lm.fEndIndex,
153            end_excluding_whitespaces: lm.fEndExcludingWhitespaces,
154            end_including_newline: lm.fEndIncludingNewline,
155            hard_break: lm.fHardBreak,
156            ascent: lm.fAscent,
157            descent: lm.fDescent,
158            unscaled_ascent: lm.fUnscaledAscent,
159            height: lm.fHeight,
160            width: lm.fWidth,
161            left: lm.fLeft,
162            baseline: lm.fBaseline,
163            line_number: lm.fLineNumber,
164            style_metrics,
165            pd: PhantomData,
166        }
167    }
168}