#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MathStyle {
Display,
DisplayCramped,
Text,
TextCramped,
Script,
ScriptCramped,
ScriptScript,
ScriptScriptCramped,
}
impl MathStyle {
pub fn cramped(self) -> Self {
match self {
Self::Display | Self::DisplayCramped => Self::DisplayCramped,
Self::Text | Self::TextCramped => Self::TextCramped,
Self::Script | Self::ScriptCramped => Self::ScriptCramped,
Self::ScriptScript | Self::ScriptScriptCramped => Self::ScriptScriptCramped,
}
}
pub fn is_display(self) -> bool {
matches!(self, Self::Display | Self::DisplayCramped)
}
pub fn is_cramped(self) -> bool {
matches!(
self,
Self::DisplayCramped
| Self::TextCramped
| Self::ScriptCramped
| Self::ScriptScriptCramped
)
}
pub fn smaller(self) -> Self {
match self {
Self::Display | Self::Text => Self::Script,
Self::DisplayCramped | Self::TextCramped => Self::ScriptCramped,
Self::Script => Self::ScriptScript,
Self::ScriptCramped => Self::ScriptScriptCramped,
Self::ScriptScript => Self::ScriptScript,
Self::ScriptScriptCramped => Self::ScriptScriptCramped,
}
}
pub fn numerator_style(self) -> Self {
match self {
Self::Display => Self::Text,
Self::DisplayCramped => Self::TextCramped,
other => other.smaller(),
}
}
pub fn denominator_style(self) -> Self {
match self {
Self::Display | Self::DisplayCramped => Self::TextCramped,
other => other.smaller().cramped(),
}
}
pub fn sup_style(self) -> Self {
self.smaller()
}
pub fn sub_style(self) -> Self {
self.smaller().cramped()
}
pub fn size_scale(self) -> f32 {
match self {
Self::Display | Self::DisplayCramped | Self::Text | Self::TextCramped => 1.0,
Self::Script | Self::ScriptCramped => 0.7,
Self::ScriptScript | Self::ScriptScriptCramped => 0.5,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct FontParams {
pub sup1: f32,
pub sup2: f32,
pub sup3: f32,
pub sub1: f32,
pub sub2: f32,
pub sup_drop: f32,
pub sub_drop: f32,
pub axis_height: f32,
pub default_rule_thickness: f32,
pub num1: f32,
pub num2: f32,
pub num3: f32,
pub denom1: f32,
pub denom2: f32,
pub big_op_spacing1: f32,
pub big_op_spacing2: f32,
pub big_op_spacing3: f32,
pub big_op_spacing4: f32,
pub big_op_spacing5: f32,
}
impl FontParams {
pub fn for_size(font_size: f32) -> Self {
Self {
sup1: 0.412892 * font_size,
sup2: 0.362892 * font_size,
sup3: 0.288889 * font_size,
sub1: 0.15 * font_size,
sub2: 0.247217 * font_size,
sup_drop: 0.386108 * font_size,
sub_drop: 0.05 * font_size,
axis_height: 0.25 * font_size,
default_rule_thickness: 0.04 * font_size,
num1: 0.677 * font_size,
num2: 0.394 * font_size,
num3: 0.444 * font_size,
denom1: 0.686 * font_size,
denom2: 0.345 * font_size,
big_op_spacing1: 0.111 * font_size,
big_op_spacing2: 0.166 * font_size,
big_op_spacing3: 0.2 * font_size,
big_op_spacing4: 0.6 * font_size,
big_op_spacing5: 0.1 * font_size,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_numerator_style_stays_full_size_at_text_level() {
assert_eq!(MathStyle::Display.numerator_style(), MathStyle::Text);
assert_eq!(MathStyle::Display.numerator_style().size_scale(), 1.0);
}
#[test]
fn denominator_style_is_always_cramped() {
assert!(MathStyle::Display.denominator_style().is_cramped());
assert!(MathStyle::Text.denominator_style().is_cramped());
}
#[test]
fn sup_style_shrinks_but_sub_style_shrinks_and_cramps() {
assert_eq!(MathStyle::Text.sup_style(), MathStyle::Script);
assert_eq!(MathStyle::Text.sub_style(), MathStyle::ScriptCramped);
}
#[test]
fn script_script_is_the_smallest_and_saturates() {
assert_eq!(MathStyle::ScriptScript.smaller(), MathStyle::ScriptScript);
assert_eq!(MathStyle::ScriptScript.size_scale(), 0.5);
}
#[test]
fn font_params_scale_linearly_with_font_size() {
let small = FontParams::for_size(10.0);
let big = FontParams::for_size(20.0);
assert!((big.axis_height - 2.0 * small.axis_height).abs() < 1e-4);
assert!((big.default_rule_thickness - 2.0 * small.default_rule_thickness).abs() < 1e-4);
}
}