From f196c1910622ee061adf70bc6e255c788d5cda20 Mon Sep 17 00:00:00 2001 From: Frederic Wang Date: Tue, 30 Aug 2022 10:16:33 +0000 Subject: [PATCH] style: Use ScriptPercentScaleDown and ScriptScriptPercentScaleDown constants The math-depth implementation is refined to take into account the ScriptPercentScaleDown and ScriptScriptPercentScaleDown constants (if the parent's first valid font has a MATH table) in order to calculate the scale factor between math-deth 0 and 1, and between 0 and 2 respectively. Behavior is unchanged if the legacy scriptsizemultiplier attribute is specified or if no MATH table is available. The preference layout.css.math-depth.enabled remains disabled in nightly until the remaining bit (support for font-size: math) is implemented in bug 1667090. Differential Revision: https://phabricator.services.mozilla.com/D91604 --- components/style/font_metrics.rs | 8 +++ components/style/properties/cascade.rs | 78 +++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/components/style/font_metrics.rs b/components/style/font_metrics.rs index 259e5693942..391d3653ee6 100644 --- a/components/style/font_metrics.rs +++ b/components/style/font_metrics.rs @@ -22,6 +22,12 @@ pub struct FontMetrics { pub ic_width: Option, /// The ascent of the font (a value is always available for this). pub ascent: Length, + /// Script scale down factor for math-depth 1. + /// https://w3c.github.io/mathml-core/#dfn-scriptpercentscaledown + pub script_percent_scale_down: Option, + /// Script scale down factor for math-depth 2. + /// https://w3c.github.io/mathml-core/#dfn-scriptscriptpercentscaledown + pub script_script_percent_scale_down: Option, } impl Default for FontMetrics { @@ -32,6 +38,8 @@ impl Default for FontMetrics { cap_height: None, ic_width: None, ascent: Length::new(0.0), + script_percent_scale_down: None, + script_script_percent_scale_down: None, } } } diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index a85d507bd18..56518c06228 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -27,6 +27,7 @@ use servo_arc::Arc; use smallvec::SmallVec; use std::borrow::Cow; use std::cell::RefCell; +use std::mem; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum CanHaveLogicalProperties { @@ -1053,8 +1054,58 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { return; } - let builder = &mut self.context.builder; + const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE : f32 = 0.71; + + // Helper function that calculates the scale factor applied to font-size + // when math-depth goes from parent_math_depth to computed_math_depth. + // This function is essentially a modification of the MathML3's formula + // 0.71^(parent_math_depth - computed_math_depth) so that a scale factor + // of parent_script_percent_scale_down is applied when math-depth goes + // from 0 to 1 and parent_script_script_percent_scale_down is applied + // when math-depth goes from 0 to 2. This is also a straightforward + // implementation of the specification's algorithm: + // https://w3c.github.io/mathml-core/#the-math-script-level-property + fn scale_factor_for_math_depth_change( + parent_math_depth: i32, + computed_math_depth: i32, + parent_script_percent_scale_down: Option, + parent_script_script_percent_scale_down: Option, + ) -> f32 { + let mut a = parent_math_depth; + let mut b = computed_math_depth; + let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE; + let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c); + let scale_between_0_and_2 = parent_script_script_percent_scale_down.unwrap_or_else(|| c * c); + let mut s = 1.0; + let mut invert_scale_factor = false; + if a == b { + return s; + } + if b < a { + mem::swap(&mut a, &mut b); + invert_scale_factor = true; + } + let mut e = b - a; + if a <= 0 && b >= 2 { + s *= scale_between_0_and_2; + e -= 2; + } else if a == 1 { + s *= scale_between_0_and_2 / scale_between_0_and_1; + e -= 1; + } else if b == 1 { + s *= scale_between_0_and_1; + e -= 1; + } + s *= (c as f32).powi(e); + if invert_scale_factor { + 1.0 / s.max(f32::MIN_POSITIVE) + } else { + s + } + } + let (new_size, new_unconstrained_size) = { + let builder = &self.context.builder; let font = builder.get_font().gecko(); let parent_font = builder.get_parent_font().gecko(); @@ -1069,7 +1120,28 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { min = builder.device.zoom_text(min); } - let scale = (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32); + // If the scriptsizemultiplier has been set to something other than + // the default scale, use MathML3's implementation for backward + // compatibility. Otherwise, follow MathML Core's algorithm. + let scale = if parent_font.mScriptSizeMultiplier != + SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE { + (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32) + } else { + builder.add_flags(ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS); + // Script scale factors are independent of orientation. + let font_metrics = self.context + .font_metrics_provider + .query(self.context, FontBaseSize::InheritedStyle, + FontMetricsOrientation::Horizontal, + true /* retrieve_math_scales */); + scale_factor_for_math_depth_change( + parent_font.mMathDepth as i32, + font.mMathDepth as i32, + font_metrics.script_percent_scale_down, + font_metrics.script_script_percent_scale_down + ) + }; + let parent_size = parent_font.mSize.0; let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0; let new_size = parent_size.scale_by(scale); @@ -1097,7 +1169,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { ) } }; - let font = builder.mutate_font().gecko_mut(); + let font = self.context.builder.mutate_font().gecko_mut(); font.mFont.size = NonNegative(new_size); font.mSize = NonNegative(new_size); font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);