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
This commit is contained in:
Frederic Wang 2022-08-30 10:16:33 +00:00 committed by Martin Robinson
parent 12a2c88605
commit f196c19106
2 changed files with 83 additions and 3 deletions

View file

@ -22,6 +22,12 @@ pub struct FontMetrics {
pub ic_width: Option<Length>, pub ic_width: Option<Length>,
/// The ascent of the font (a value is always available for this). /// The ascent of the font (a value is always available for this).
pub ascent: Length, 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<f32>,
/// Script scale down factor for math-depth 2.
/// https://w3c.github.io/mathml-core/#dfn-scriptscriptpercentscaledown
pub script_script_percent_scale_down: Option<f32>,
} }
impl Default for FontMetrics { impl Default for FontMetrics {
@ -32,6 +38,8 @@ impl Default for FontMetrics {
cap_height: None, cap_height: None,
ic_width: None, ic_width: None,
ascent: Length::new(0.0), ascent: Length::new(0.0),
script_percent_scale_down: None,
script_script_percent_scale_down: None,
} }
} }
} }

View file

@ -27,6 +27,7 @@ use servo_arc::Arc;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::mem;
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum CanHaveLogicalProperties { enum CanHaveLogicalProperties {
@ -1053,8 +1054,58 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
return; 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<f32>,
parent_script_script_percent_scale_down: Option<f32>,
) -> 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 (new_size, new_unconstrained_size) = {
let builder = &self.context.builder;
let font = builder.get_font().gecko(); let font = builder.get_font().gecko();
let parent_font = builder.get_parent_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); 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_size = parent_font.mSize.0;
let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0; let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
let new_size = parent_size.scale_by(scale); 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.mFont.size = NonNegative(new_size);
font.mSize = NonNegative(new_size); font.mSize = NonNegative(new_size);
font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size); font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);