Skip to main content

latex_rust/layout/
metrics.rs

1//! OpenType MATH constants as [`Dim`](crate::Dim). TeX σ-names are documented on fields.
2
3use crate::dim::Dim;
4use crate::error::Error;
5use crate::font::MathFont;
6use crate::layout::style::MathStyle;
7
8/// MATH-table parameters used by the layout engine.
9///
10/// # Examples
11///
12/// ```
13/// use latex_rust::{MathFont, MathParams, MathStyle};
14///
15/// let font = MathFont::stix_two_math().unwrap();
16/// let p = MathParams::from_font(&font).unwrap();
17/// assert!(!p.axis_height.is_zero());
18/// assert!(!p.em(MathStyle::Text).is_zero());
19/// ```
20#[derive(Clone, Debug)]
21pub struct MathParams {
22    /// σ1: x-height (em).
23    pub x_height: Dim,
24    /// σ2: quad / em width.
25    pub quad: Dim,
26    /// σ6 / σ22: math axis height.
27    pub axis_height: Dim,
28    /// Accent base height.
29    pub accent_base_height: Dim,
30    /// Flattened accent base height (cramped / tall bases).
31    pub flattened_accent_base_height: Dim,
32    /// σ8: default rule thickness (fraction / radical bar).
33    pub fraction_rule_thickness: Dim,
34    /// σ10 analogue: numerator shift (text).
35    pub fraction_numerator_shift_up: Dim,
36    /// σ9 analogue: numerator shift (display).
37    pub fraction_numerator_display_style_shift_up: Dim,
38    /// σ12 analogue: denominator shift (text).
39    pub fraction_denominator_shift_down: Dim,
40    /// σ11 analogue: denominator shift (display).
41    pub fraction_denominator_display_style_shift_down: Dim,
42    /// Minimum gap num ↔ rule (text).
43    pub fraction_numerator_gap_min: Dim,
44    /// Minimum gap num ↔ rule (display).
45    pub fraction_num_display_style_gap_min: Dim,
46    /// Minimum gap rule ↔ den (text).
47    pub fraction_denominator_gap_min: Dim,
48    /// Minimum gap rule ↔ den (display).
49    pub fraction_denom_display_style_gap_min: Dim,
50    /// σ14 analogue: superscript shift (text).
51    pub superscript_shift_up: Dim,
52    /// σ15 analogue: superscript shift cramped.
53    pub superscript_shift_up_cramped: Dim,
54    /// σ16 analogue: subscript shift.
55    pub subscript_shift_down: Dim,
56    /// Minimum gap between sub and sup.
57    pub sub_superscript_gap_min: Dim,
58    /// Space after a script.
59    pub space_after_script: Dim,
60    /// Radical vertical gap (text).
61    pub radical_vertical_gap: Dim,
62    /// Radical vertical gap (display).
63    pub radical_display_style_vertical_gap: Dim,
64    /// Radical rule thickness.
65    pub radical_rule_thickness: Dim,
66    /// Extra ascender above radical rule.
67    pub radical_extra_ascender: Dim,
68    /// Kern before a radical degree.
69    pub radical_kern_before_degree: Dim,
70    /// Kern after a radical degree.
71    pub radical_kern_after_degree: Dim,
72    /// Degree bottom raise percent (integer 0–100).
73    pub radical_degree_bottom_raise_percent: i16,
74    /// Overbar gap.
75    pub overbar_vertical_gap: Dim,
76    /// Overbar rule thickness.
77    pub overbar_rule_thickness: Dim,
78    /// Overbar extra ascender.
79    pub overbar_extra_ascender: Dim,
80    /// Underbar gap.
81    pub underbar_vertical_gap: Dim,
82    /// Underbar rule thickness.
83    pub underbar_rule_thickness: Dim,
84    /// Underbar extra descender.
85    pub underbar_extra_descender: Dim,
86    /// Upper limit gap min.
87    pub upper_limit_gap_min: Dim,
88    /// Upper limit baseline rise min.
89    pub upper_limit_baseline_rise_min: Dim,
90    /// Lower limit gap min.
91    pub lower_limit_gap_min: Dim,
92    /// Lower limit baseline drop min.
93    pub lower_limit_baseline_drop_min: Dim,
94    /// Display operator min height (font units, as Dim em).
95    pub display_operator_min_height: Dim,
96    /// Script scale (percent, e.g. 70).
97    pub script_percent_scale_down: i16,
98    /// Scriptscript scale (percent, e.g. 55).
99    pub script_script_percent_scale_down: i16,
100    /// `unitsPerEm`.
101    pub units_per_em: u16,
102}
103
104impl MathParams {
105    /// Load MATH constants from `font`. Missing MATH is [`Error::Unsupported`].
106    ///
107    /// # Errors
108    ///
109    /// [`Error::Unsupported`] if the face has no MATH table or constants.
110    pub fn from_font(font: &MathFont) -> Result<Self, Error> {
111        let face = font.face();
112        let math = face.tables().math.ok_or_else(|| Error::Unsupported {
113            what: "OpenType MATH table".into(),
114        })?;
115        let c = math.constants.ok_or_else(|| Error::Unsupported {
116            what: "MATH constants".into(),
117        })?;
118        let upem = font.units_per_em();
119        let fu = |v: i16| Dim::from_font_units(i64::from(v), upem);
120        let fu_u = |v: u16| Dim::from_font_units(i64::from(v), upem);
121        let xh = face.x_height().map(fu).unwrap_or_else(|| Dim::ratio(1, 2));
122        Ok(Self {
123            x_height: xh,
124            quad: Dim::one(),
125            axis_height: fu(c.axis_height().value),
126            accent_base_height: fu(c.accent_base_height().value),
127            flattened_accent_base_height: fu(c.flattened_accent_base_height().value),
128            fraction_rule_thickness: fu(c.fraction_rule_thickness().value),
129            fraction_numerator_shift_up: fu(c.fraction_numerator_shift_up().value),
130            fraction_numerator_display_style_shift_up: fu(c
131                .fraction_numerator_display_style_shift_up()
132                .value),
133            fraction_denominator_shift_down: fu(c.fraction_denominator_shift_down().value),
134            fraction_denominator_display_style_shift_down: fu(c
135                .fraction_denominator_display_style_shift_down()
136                .value),
137            fraction_numerator_gap_min: fu(c.fraction_numerator_gap_min().value),
138            fraction_num_display_style_gap_min: fu(c.fraction_num_display_style_gap_min().value),
139            fraction_denominator_gap_min: fu(c.fraction_denominator_gap_min().value),
140            fraction_denom_display_style_gap_min: fu(c
141                .fraction_denom_display_style_gap_min()
142                .value),
143            superscript_shift_up: fu(c.superscript_shift_up().value),
144            superscript_shift_up_cramped: fu(c.superscript_shift_up_cramped().value),
145            subscript_shift_down: fu(c.subscript_shift_down().value),
146            sub_superscript_gap_min: fu(c.sub_superscript_gap_min().value),
147            space_after_script: fu(c.space_after_script().value),
148            radical_vertical_gap: fu(c.radical_vertical_gap().value),
149            radical_display_style_vertical_gap: fu(c.radical_display_style_vertical_gap().value),
150            radical_rule_thickness: fu(c.radical_rule_thickness().value),
151            radical_extra_ascender: fu(c.radical_extra_ascender().value),
152            radical_kern_before_degree: fu(c.radical_kern_before_degree().value),
153            radical_kern_after_degree: fu(c.radical_kern_after_degree().value),
154            radical_degree_bottom_raise_percent: c.radical_degree_bottom_raise_percent(),
155            overbar_vertical_gap: fu(c.overbar_vertical_gap().value),
156            overbar_rule_thickness: fu(c.overbar_rule_thickness().value),
157            overbar_extra_ascender: fu(c.overbar_extra_ascender().value),
158            underbar_vertical_gap: fu(c.underbar_vertical_gap().value),
159            underbar_rule_thickness: fu(c.underbar_rule_thickness().value),
160            underbar_extra_descender: fu(c.underbar_extra_descender().value),
161            upper_limit_gap_min: fu(c.upper_limit_gap_min().value),
162            upper_limit_baseline_rise_min: fu(c.upper_limit_baseline_rise_min().value),
163            lower_limit_gap_min: fu(c.lower_limit_gap_min().value),
164            lower_limit_baseline_drop_min: fu(c.lower_limit_baseline_drop_min().value),
165            display_operator_min_height: fu_u(c.display_operator_min_height()),
166            script_percent_scale_down: c.script_percent_scale_down(),
167            script_script_percent_scale_down: c.script_script_percent_scale_down(),
168            units_per_em: upem,
169        })
170    }
171
172    /// Scale factor for `style` (1, script%, or scriptscript%).
173    #[must_use]
174    pub fn scale(&self, style: MathStyle) -> Dim {
175        match style.script_level() {
176            0 => Dim::one(),
177            1 => Dim::from_i64(i64::from(self.script_percent_scale_down)) / Dim::from_i64(100),
178            _ => {
179                Dim::from_i64(i64::from(self.script_script_percent_scale_down)) / Dim::from_i64(100)
180            }
181        }
182    }
183
184    /// Current em (`quad * scale`).
185    #[must_use]
186    pub fn em(&self, style: MathStyle) -> Dim {
187        &self.quad * &self.scale(style)
188    }
189
190    /// One mu at `style` (`em / 18`).
191    #[must_use]
192    pub fn mu(&self, style: MathStyle) -> Dim {
193        self.em(style) / Dim::from_i64(18)
194    }
195}