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