2023-01-22 13:26:42 +01:00

170 lines
4.5 KiB
Rust

use ttf_parser::math::MathValue;
use super::*;
macro_rules! scaled {
($ctx:expr, text: $text:ident, display: $display:ident $(,)?) => {
match $ctx.style.size {
MathSize::Display => scaled!($ctx, $display),
_ => scaled!($ctx, $text),
}
};
($ctx:expr, $name:ident) => {
$ctx.constants.$name().scaled($ctx)
};
}
macro_rules! percent {
($ctx:expr, $name:ident) => {
$ctx.constants.$name() as f64 / 100.0
};
}
/// The context for math layout.
pub(super) struct MathContext<'a, 'b, 'v> {
pub vt: &'v mut Vt<'b>,
pub outer: StyleChain<'a>,
pub map: StyleMap,
pub regions: Regions<'a>,
pub font: &'a Font,
pub ttf: &'a ttf_parser::Face<'a>,
pub table: ttf_parser::math::Table<'a>,
pub constants: ttf_parser::math::Constants<'a>,
pub fill: Paint,
pub lang: Lang,
pub row: MathRow,
pub style: MathStyle,
base_size: Abs,
scaled_size: Abs,
style_stack: Vec<MathStyle>,
}
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
pub fn new(
vt: &'v mut Vt<'b>,
styles: StyleChain<'a>,
regions: Regions,
font: &'a Font,
block: bool,
) -> Self {
let table = font.ttf().tables().math.unwrap();
let constants = table.constants.unwrap();
let size = styles.get(TextNode::SIZE);
Self {
vt,
outer: styles,
map: StyleMap::new(),
regions: {
let size = Size::new(regions.first.x, regions.base.y);
Regions::one(size, regions.base, Axes::splat(false))
},
style: MathStyle {
variant: MathVariant::Serif,
size: if block { MathSize::Display } else { MathSize::Text },
cramped: false,
bold: false,
italic: true,
},
fill: styles.get(TextNode::FILL),
lang: styles.get(TextNode::LANG),
font: &font,
ttf: font.ttf(),
table,
constants,
row: MathRow::new(),
base_size: size,
scaled_size: size,
style_stack: vec![],
}
}
pub fn push(&mut self, fragment: impl Into<MathFragment>) {
self.row.push(self.scaled_size, self.style, fragment);
}
pub fn layout_non_math(&mut self, content: &Content) -> SourceResult<Frame> {
Ok(content
.layout(&mut self.vt, self.outer.chain(&self.map), self.regions)?
.into_frame())
}
pub fn layout_fragment(
&mut self,
node: &dyn LayoutMath,
) -> SourceResult<MathFragment> {
let row = self.layout_row(node)?;
Ok(if row.0.len() == 1 {
row.0.into_iter().next().unwrap()
} else {
row.to_frame(self).into()
})
}
pub fn layout_row(&mut self, node: &dyn LayoutMath) -> SourceResult<MathRow> {
let prev = std::mem::take(&mut self.row);
node.layout_math(self)?;
Ok(std::mem::replace(&mut self.row, prev))
}
pub fn layout_frame(&mut self, node: &dyn LayoutMath) -> SourceResult<Frame> {
Ok(self.layout_fragment(node)?.to_frame(self))
}
pub fn size(&self) -> Abs {
self.scaled_size
}
pub fn style(&mut self, style: MathStyle) {
self.style_stack.push(self.style);
self.style = style;
self.rescale();
self.map.set(TextNode::SIZE, TextSize(self.scaled_size.into()));
}
pub fn unstyle(&mut self) {
self.style = self.style_stack.pop().unwrap();
self.rescale();
self.map.unset();
}
fn rescale(&mut self) {
self.scaled_size = match self.style.size {
MathSize::Display | MathSize::Text => self.base_size,
MathSize::Script => {
self.base_size * percent!(self, script_percent_scale_down)
}
MathSize::ScriptScript => {
self.base_size * percent!(self, script_script_percent_scale_down)
}
};
}
}
pub(super) trait Scaled {
fn scaled(self, ctx: &MathContext) -> Abs;
}
impl Scaled for i16 {
fn scaled(self, ctx: &MathContext) -> Abs {
ctx.font.to_em(self).scaled(ctx)
}
}
impl Scaled for u16 {
fn scaled(self, ctx: &MathContext) -> Abs {
ctx.font.to_em(self).scaled(ctx)
}
}
impl Scaled for Em {
fn scaled(self, ctx: &MathContext) -> Abs {
self.at(ctx.size())
}
}
impl Scaled for MathValue<'_> {
fn scaled(self, ctx: &MathContext) -> Abs {
self.value.scaled(ctx)
}
}