mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Fix math styling
This commit is contained in:
parent
8fbfa594e0
commit
60dfe8f893
@ -1,4 +1,5 @@
|
|||||||
use ttf_parser::math::MathValue;
|
use ttf_parser::math::MathValue;
|
||||||
|
use typst::font::{FontStyle, FontWeight};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -24,21 +25,18 @@ macro_rules! percent {
|
|||||||
/// The context for math layout.
|
/// The context for math layout.
|
||||||
pub(super) struct MathContext<'a, 'b, 'v> {
|
pub(super) struct MathContext<'a, 'b, 'v> {
|
||||||
pub vt: &'v mut Vt<'b>,
|
pub vt: &'v mut Vt<'b>,
|
||||||
pub outer: StyleChain<'a>,
|
|
||||||
pub map: StyleMap,
|
|
||||||
pub regions: Regions<'a>,
|
pub regions: Regions<'a>,
|
||||||
pub font: &'a Font,
|
pub font: &'a Font,
|
||||||
pub ttf: &'a ttf_parser::Face<'a>,
|
pub ttf: &'a ttf_parser::Face<'a>,
|
||||||
pub table: ttf_parser::math::Table<'a>,
|
pub table: ttf_parser::math::Table<'a>,
|
||||||
pub constants: ttf_parser::math::Constants<'a>,
|
pub constants: ttf_parser::math::Constants<'a>,
|
||||||
pub space_width: Em,
|
pub space_width: Em,
|
||||||
pub fill: Paint,
|
|
||||||
pub lang: Lang,
|
|
||||||
pub row: MathRow,
|
pub row: MathRow,
|
||||||
|
pub map: StyleMap,
|
||||||
pub style: MathStyle,
|
pub style: MathStyle,
|
||||||
base_size: Abs,
|
pub size: Abs,
|
||||||
scaled_size: Abs,
|
outer: StyleChain<'a>,
|
||||||
style_stack: Vec<MathStyle>,
|
style_stack: Vec<(MathStyle, Abs)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
||||||
@ -52,7 +50,6 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
let table = font.ttf().tables().math.unwrap();
|
let table = font.ttf().tables().math.unwrap();
|
||||||
let constants = table.constants.unwrap();
|
let constants = table.constants.unwrap();
|
||||||
let size = styles.get(TextNode::SIZE);
|
let size = styles.get(TextNode::SIZE);
|
||||||
|
|
||||||
let ttf = font.ttf();
|
let ttf = font.ttf();
|
||||||
let space_width = ttf
|
let space_width = ttf
|
||||||
.glyph_index(' ')
|
.glyph_index(' ')
|
||||||
@ -60,38 +57,38 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
.map(|advance| font.to_em(advance))
|
.map(|advance| font.to_em(advance))
|
||||||
.unwrap_or(THICK);
|
.unwrap_or(THICK);
|
||||||
|
|
||||||
|
let variant = variant(styles);
|
||||||
Self {
|
Self {
|
||||||
vt,
|
vt,
|
||||||
outer: styles,
|
|
||||||
map: StyleMap::new(),
|
|
||||||
regions: {
|
regions: {
|
||||||
let size = Size::new(regions.first.x, regions.base.y);
|
let size = Size::new(regions.first.x, regions.base.y);
|
||||||
Regions::one(size, regions.base, Axes::splat(false))
|
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,
|
font: &font,
|
||||||
ttf: font.ttf(),
|
ttf: font.ttf(),
|
||||||
table,
|
table,
|
||||||
constants,
|
constants,
|
||||||
space_width,
|
space_width,
|
||||||
row: MathRow::new(),
|
row: MathRow::new(),
|
||||||
base_size: size,
|
map: StyleMap::new(),
|
||||||
scaled_size: size,
|
style: MathStyle {
|
||||||
|
variant: MathVariant::Serif,
|
||||||
|
size: if block { MathSize::Display } else { MathSize::Text },
|
||||||
|
cramped: false,
|
||||||
|
bold: variant.weight >= FontWeight::BOLD,
|
||||||
|
italic: match variant.style {
|
||||||
|
FontStyle::Normal => Smart::Auto,
|
||||||
|
FontStyle::Italic | FontStyle::Oblique => Smart::Custom(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
size,
|
||||||
|
outer: styles,
|
||||||
style_stack: vec![],
|
style_stack: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, fragment: impl Into<MathFragment>) {
|
pub fn push(&mut self, fragment: impl Into<MathFragment>) {
|
||||||
self.row
|
self.row.push(self.size, self.space_width, self.style, fragment);
|
||||||
.push(self.scaled_size, self.space_width, self.style, fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extend(&mut self, row: MathRow) {
|
pub fn extend(&mut self, row: MathRow) {
|
||||||
@ -130,11 +127,12 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
Ok(self.layout_fragment(node)?.to_frame(self))
|
Ok(self.layout_fragment(node)?.to_frame(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout_text(&mut self, text: &EcoString) -> SourceResult<()> {
|
pub fn layout_text(&mut self, text: &str) -> SourceResult<()> {
|
||||||
let mut chars = text.chars();
|
let mut chars = text.chars();
|
||||||
if let Some(glyph) = chars
|
if let Some(glyph) = chars
|
||||||
.next()
|
.next()
|
||||||
.filter(|_| chars.next().is_none())
|
.filter(|_| chars.next().is_none())
|
||||||
|
.map(|c| self.style.styled_char(c))
|
||||||
.and_then(|c| GlyphFragment::try_new(self, c))
|
.and_then(|c| GlyphFragment::try_new(self, c))
|
||||||
{
|
{
|
||||||
// A single letter that is available in the math font.
|
// A single letter that is available in the math font.
|
||||||
@ -147,10 +145,10 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
self.push(glyph);
|
self.push(glyph);
|
||||||
}
|
}
|
||||||
} else if text.chars().all(|c| c.is_ascii_digit()) {
|
} else if text.chars().all(|c| c.is_ascii_digit()) {
|
||||||
// A number that should respect math styling and can therefore
|
// Numbers aren't that difficult.
|
||||||
// not fall back to the normal text layout.
|
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
for c in text.chars() {
|
for c in text.chars() {
|
||||||
|
let c = self.style.styled_char(c);
|
||||||
vec.push(GlyphFragment::new(self, c).into());
|
vec.push(GlyphFragment::new(self, c).into());
|
||||||
}
|
}
|
||||||
let frame = MathRow(vec).to_frame(self);
|
let frame = MathRow(vec).to_frame(self);
|
||||||
@ -158,7 +156,12 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
} else {
|
} else {
|
||||||
// Anything else is handled by Typst's standard text layout.
|
// Anything else is handled by Typst's standard text layout.
|
||||||
let spaced = text.graphemes(true).count() > 1;
|
let spaced = text.graphemes(true).count() > 1;
|
||||||
let frame = self.layout_non_math(&TextNode::packed(text.clone()))?;
|
let mut style = self.style;
|
||||||
|
if self.style.italic == Smart::Auto {
|
||||||
|
style = style.with_italic(false);
|
||||||
|
}
|
||||||
|
let text: EcoString = text.chars().map(|c| style.styled_char(c)).collect();
|
||||||
|
let frame = self.layout_non_math(&TextNode::packed(text))?;
|
||||||
self.push(
|
self.push(
|
||||||
FrameFragment::new(frame)
|
FrameFragment::new(frame)
|
||||||
.with_class(MathClass::Alphabetic)
|
.with_class(MathClass::Alphabetic)
|
||||||
@ -169,33 +172,35 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> Abs {
|
pub fn styles(&self) -> StyleChain {
|
||||||
self.scaled_size
|
self.outer.chain(&self.map)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style(&mut self, style: MathStyle) {
|
pub fn style(&mut self, style: MathStyle) {
|
||||||
self.style_stack.push(self.style);
|
self.style_stack.push((self.style, self.size));
|
||||||
|
let base_size = self.styles().get(TextNode::SIZE) / self.style.size.factor(self);
|
||||||
|
self.size = base_size * style.size.factor(self);
|
||||||
|
self.map.set(TextNode::SIZE, TextSize(self.size.into()));
|
||||||
|
self.map.set(
|
||||||
|
TextNode::STYLE,
|
||||||
|
if style.italic == Smart::Custom(true) {
|
||||||
|
FontStyle::Italic
|
||||||
|
} else {
|
||||||
|
FontStyle::Normal
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.map.set(
|
||||||
|
TextNode::WEIGHT,
|
||||||
|
if style.bold { FontWeight::BOLD } else { FontWeight::REGULAR },
|
||||||
|
);
|
||||||
self.style = style;
|
self.style = style;
|
||||||
self.rescale();
|
|
||||||
self.map.set(TextNode::SIZE, TextSize(self.scaled_size.into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unstyle(&mut self) {
|
pub fn unstyle(&mut self) {
|
||||||
self.style = self.style_stack.pop().unwrap();
|
(self.style, self.size) = self.style_stack.pop().unwrap();
|
||||||
self.rescale();
|
self.map.unset();
|
||||||
|
self.map.unset();
|
||||||
self.map.unset();
|
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)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +222,7 @@ impl Scaled for u16 {
|
|||||||
|
|
||||||
impl Scaled for Em {
|
impl Scaled for Em {
|
||||||
fn scaled(self, ctx: &MathContext) -> Abs {
|
fn scaled(self, ctx: &MathContext) -> Abs {
|
||||||
self.at(ctx.size())
|
self.at(ctx.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +157,10 @@ fn layout(
|
|||||||
frame.push(
|
frame.push(
|
||||||
line_pos,
|
line_pos,
|
||||||
Element::Shape(
|
Element::Shape(
|
||||||
Geometry::Line(Point::with_x(line_width))
|
Geometry::Line(Point::with_x(line_width)).stroked(Stroke {
|
||||||
.stroked(Stroke { paint: ctx.fill, thickness }),
|
paint: ctx.styles().get(TextNode::FILL),
|
||||||
|
thickness,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
ctx.push(frame);
|
ctx.push(frame);
|
||||||
|
@ -121,6 +121,8 @@ impl From<Frame> for MathFragment {
|
|||||||
pub(super) struct GlyphFragment {
|
pub(super) struct GlyphFragment {
|
||||||
pub id: GlyphId,
|
pub id: GlyphId,
|
||||||
pub c: char,
|
pub c: char,
|
||||||
|
pub lang: Lang,
|
||||||
|
pub fill: Paint,
|
||||||
pub font_size: Abs,
|
pub font_size: Abs,
|
||||||
pub width: Abs,
|
pub width: Abs,
|
||||||
pub ascent: Abs,
|
pub ascent: Abs,
|
||||||
@ -131,7 +133,6 @@ pub(super) struct GlyphFragment {
|
|||||||
|
|
||||||
impl GlyphFragment {
|
impl GlyphFragment {
|
||||||
pub fn new(ctx: &MathContext, c: char) -> Self {
|
pub fn new(ctx: &MathContext, c: char) -> Self {
|
||||||
let c = ctx.style.styled_char(c);
|
|
||||||
let id = ctx.ttf.glyph_index(c).unwrap_or_default();
|
let id = ctx.ttf.glyph_index(c).unwrap_or_default();
|
||||||
Self::with_id(ctx, c, id)
|
Self::with_id(ctx, c, id)
|
||||||
}
|
}
|
||||||
@ -154,7 +155,9 @@ impl GlyphFragment {
|
|||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
c,
|
c,
|
||||||
font_size: ctx.size(),
|
lang: ctx.styles().get(TextNode::LANG),
|
||||||
|
fill: ctx.styles().get(TextNode::FILL),
|
||||||
|
font_size: ctx.size,
|
||||||
width: advance.scaled(ctx),
|
width: advance.scaled(ctx),
|
||||||
ascent: bbox.y_max.scaled(ctx),
|
ascent: bbox.y_max.scaled(ctx),
|
||||||
descent: -bbox.y_min.scaled(ctx),
|
descent: -bbox.y_min.scaled(ctx),
|
||||||
@ -184,12 +187,12 @@ impl GlyphFragment {
|
|||||||
let text = Text {
|
let text = Text {
|
||||||
font: ctx.font.clone(),
|
font: ctx.font.clone(),
|
||||||
size: self.font_size,
|
size: self.font_size,
|
||||||
fill: ctx.fill,
|
fill: self.fill,
|
||||||
lang: ctx.lang,
|
lang: self.lang,
|
||||||
glyphs: vec![Glyph {
|
glyphs: vec![Glyph {
|
||||||
id: self.id.0,
|
id: self.id.0,
|
||||||
c: self.c,
|
c: self.c,
|
||||||
x_advance: Em::from_length(self.width, ctx.size()),
|
x_advance: Em::from_length(self.width, ctx.size),
|
||||||
x_offset: Em::zero(),
|
x_offset: Em::zero(),
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
@ -66,7 +66,7 @@ impl LayoutMath for LrNode {
|
|||||||
let height = self
|
let height = self
|
||||||
.size
|
.size
|
||||||
.unwrap_or(Rel::one())
|
.unwrap_or(Rel::one())
|
||||||
.resolve(ctx.outer.chain(&ctx.map))
|
.resolve(ctx.styles())
|
||||||
.relative_to(2.0 * max_extent);
|
.relative_to(2.0 * max_extent);
|
||||||
|
|
||||||
match row.0.as_mut_slice() {
|
match row.0.as_mut_slice() {
|
||||||
|
@ -39,20 +39,11 @@ impl VecNode {
|
|||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.all()?).pack())
|
Ok(Self(args.all()?).pack())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
|
||||||
match name {
|
|
||||||
"elements" => {
|
|
||||||
Some(Value::Array(self.0.iter().cloned().map(Value::Content).collect()))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutMath for VecNode {
|
impl LayoutMath for VecNode {
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
let delim = ctx.outer.get(Self::DELIM);
|
let delim = ctx.styles().get(Self::DELIM);
|
||||||
layout(ctx, &self.0, Align::Center, Some(delim.open()), Some(delim.close()))
|
layout(ctx, &self.0, Align::Center, Some(delim.open()), Some(delim.close()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,15 +77,6 @@ impl CasesNode {
|
|||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
Ok(Self(args.all()?).pack())
|
Ok(Self(args.all()?).pack())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
|
||||||
match name {
|
|
||||||
"branches" => {
|
|
||||||
Some(Value::Array(self.0.iter().cloned().map(Value::Content).collect()))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutMath for CasesNode {
|
impl LayoutMath for CasesNode {
|
||||||
|
@ -29,29 +29,27 @@ pub use self::root::*;
|
|||||||
pub use self::stack::*;
|
pub use self::stack::*;
|
||||||
pub use self::style::*;
|
pub use self::style::*;
|
||||||
|
|
||||||
use ttf_parser::GlyphId;
|
use ttf_parser::{GlyphId, Rect};
|
||||||
use ttf_parser::Rect;
|
|
||||||
use typst::font::Font;
|
use typst::font::Font;
|
||||||
use typst::model::{Guard, Module, Scope, SequenceNode};
|
use typst::model::{Guard, Module, Scope, SequenceNode, StyledNode};
|
||||||
use unicode_math_class::MathClass;
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use self::ctx::*;
|
use self::ctx::*;
|
||||||
use self::fragment::*;
|
use self::fragment::*;
|
||||||
use self::row::*;
|
use self::row::*;
|
||||||
use self::spacing::*;
|
use self::spacing::*;
|
||||||
use crate::layout::HNode;
|
use crate::layout::{HNode, ParNode, Spacing};
|
||||||
use crate::layout::ParNode;
|
|
||||||
use crate::layout::Spacing;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::LinebreakNode;
|
use crate::text::{
|
||||||
use crate::text::TextNode;
|
families, variant, FallbackList, FontFamily, LinebreakNode, SpaceNode, TextNode,
|
||||||
use crate::text::TextSize;
|
TextSize,
|
||||||
use crate::text::{families, variant, FallbackList, FontFamily, SpaceNode};
|
};
|
||||||
|
|
||||||
/// Create a module with all math definitions.
|
/// Create a module with all math definitions.
|
||||||
pub fn module(sym: &Module) -> Module {
|
pub fn module(sym: &Module) -> Module {
|
||||||
let mut math = Scope::deduplicating();
|
let mut math = Scope::deduplicating();
|
||||||
math.def_func::<FormulaNode>("formula");
|
math.def_func::<FormulaNode>("formula");
|
||||||
|
math.def_func::<TextNode>("text");
|
||||||
|
|
||||||
// Grouping.
|
// Grouping.
|
||||||
math.def_func::<LrNode>("lr");
|
math.def_func::<LrNode>("lr");
|
||||||
@ -83,6 +81,7 @@ pub fn module(sym: &Module) -> Module {
|
|||||||
math.def_func::<RootNode>("root");
|
math.def_func::<RootNode>("root");
|
||||||
|
|
||||||
// Styles.
|
// Styles.
|
||||||
|
math.def_func::<UprightNode>("upright");
|
||||||
math.def_func::<BoldNode>("bold");
|
math.def_func::<BoldNode>("bold");
|
||||||
math.def_func::<ItalicNode>("italic");
|
math.def_func::<ItalicNode>("italic");
|
||||||
math.def_func::<SerifNode>("serif");
|
math.def_func::<SerifNode>("serif");
|
||||||
@ -243,6 +242,24 @@ impl LayoutMath for FormulaNode {
|
|||||||
|
|
||||||
impl LayoutMath for Content {
|
impl LayoutMath for Content {
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
|
if let Some(node) = self.to::<SequenceNode>() {
|
||||||
|
for child in &node.0 {
|
||||||
|
child.layout_math(ctx)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(styled) = self.to::<StyledNode>() {
|
||||||
|
let prev_map = std::mem::replace(&mut ctx.map, styled.map.clone());
|
||||||
|
let prev_size = ctx.size;
|
||||||
|
ctx.map.apply(prev_map.clone());
|
||||||
|
ctx.size = ctx.styles().get(TextNode::SIZE);
|
||||||
|
styled.sub.layout_math(ctx)?;
|
||||||
|
ctx.size = prev_size;
|
||||||
|
ctx.map = prev_map;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if self.is::<SpaceNode>() {
|
if self.is::<SpaceNode>() {
|
||||||
ctx.push(MathFragment::Space);
|
ctx.push(MathFragment::Space);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -256,9 +273,7 @@ impl LayoutMath for Content {
|
|||||||
if let Some(node) = self.to::<HNode>() {
|
if let Some(node) = self.to::<HNode>() {
|
||||||
if let Spacing::Relative(rel) = node.amount {
|
if let Spacing::Relative(rel) = node.amount {
|
||||||
if rel.rel.is_zero() {
|
if rel.rel.is_zero() {
|
||||||
ctx.push(MathFragment::Spacing(
|
ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles())));
|
||||||
rel.abs.resolve(ctx.outer.chain(&ctx.map)),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -269,13 +284,6 @@ impl LayoutMath for Content {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(node) = self.to::<SequenceNode>() {
|
|
||||||
for child in &node.0 {
|
|
||||||
child.layout_math(ctx)?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(node) = self.with::<dyn LayoutMath>() {
|
if let Some(node) = self.with::<dyn LayoutMath>() {
|
||||||
return node.layout_math(ctx);
|
return node.layout_math(ctx);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ fn layout(
|
|||||||
line_pos,
|
line_pos,
|
||||||
Element::Shape(
|
Element::Shape(
|
||||||
Geometry::Line(Point::with_x(line_length))
|
Geometry::Line(Point::with_x(line_length))
|
||||||
.stroked(Stroke { paint: ctx.fill, thickness }),
|
.stroked(Stroke { paint: ctx.styles().get(TextNode::FILL), thickness }),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
frame.push_frame(radicand_pos, radicand);
|
frame.push_frame(radicand_pos, radicand);
|
||||||
|
@ -77,7 +77,7 @@ impl MathRow {
|
|||||||
let mut frame = Frame::new(Size::zero());
|
let mut frame = Frame::new(Size::zero());
|
||||||
let fragments = std::mem::take(&mut self.0);
|
let fragments = std::mem::take(&mut self.0);
|
||||||
|
|
||||||
let leading = ctx.outer.chain(&ctx.map).get(ParNode::LEADING);
|
let leading = ctx.styles().get(ParNode::LEADING);
|
||||||
let rows: Vec<_> = fragments
|
let rows: Vec<_> = fragments
|
||||||
.split(|frag| matches!(frag, MathFragment::Linebreak))
|
.split(|frag| matches!(frag, MathFragment::Linebreak))
|
||||||
.map(|slice| Self(slice.to_vec()))
|
.map(|slice| Self(slice.to_vec()))
|
||||||
|
@ -1,5 +1,40 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
/// # Upright
|
||||||
|
/// Upright (non-italic) font style in math.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ upright(A) != A $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - body: Content (positional, required)
|
||||||
|
/// The piece of formula to style.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(LayoutMath)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct UprightNode(pub Content);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl UprightNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
Ok(Self(args.expect("body")?).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutMath for UprightNode {
|
||||||
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
|
ctx.style(ctx.style.with_italic(false));
|
||||||
|
self.0.layout_math(ctx)?;
|
||||||
|
ctx.unstyle();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # Bold
|
/// # Bold
|
||||||
/// Bold font style in math.
|
/// Bold font style in math.
|
||||||
///
|
///
|
||||||
@ -28,7 +63,7 @@ impl BoldNode {
|
|||||||
|
|
||||||
impl LayoutMath for BoldNode {
|
impl LayoutMath for BoldNode {
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
ctx.style(ctx.style.with_italic(false).with_bold_toggled());
|
ctx.style(ctx.style.with_bold(true));
|
||||||
self.0.layout_math(ctx)?;
|
self.0.layout_math(ctx)?;
|
||||||
ctx.unstyle();
|
ctx.unstyle();
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -60,7 +95,7 @@ impl ItalicNode {
|
|||||||
|
|
||||||
impl LayoutMath for ItalicNode {
|
impl LayoutMath for ItalicNode {
|
||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
ctx.style(ctx.style.with_italic_toggled());
|
ctx.style(ctx.style.with_italic(true));
|
||||||
self.0.layout_math(ctx)?;
|
self.0.layout_math(ctx)?;
|
||||||
ctx.unstyle();
|
ctx.unstyle();
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -290,22 +325,7 @@ pub struct MathStyle {
|
|||||||
/// Whether to use bold glyphs.
|
/// Whether to use bold glyphs.
|
||||||
pub bold: bool,
|
pub bold: bool,
|
||||||
/// Whether to use italic glyphs.
|
/// Whether to use italic glyphs.
|
||||||
pub italic: bool,
|
pub italic: Smart<bool>,
|
||||||
}
|
|
||||||
|
|
||||||
/// The size of elements in a formula.
|
|
||||||
///
|
|
||||||
/// See the TeXbook p. 141.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
||||||
pub enum MathSize {
|
|
||||||
/// Second-level sub- and superscripts.
|
|
||||||
ScriptScript,
|
|
||||||
/// Sub- and superscripts.
|
|
||||||
Script,
|
|
||||||
/// Math in text.
|
|
||||||
Text,
|
|
||||||
/// Math on its own line.
|
|
||||||
Display,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MathStyle {
|
impl MathStyle {
|
||||||
@ -331,17 +351,7 @@ impl MathStyle {
|
|||||||
|
|
||||||
/// This style, with `italic` set to the given value.
|
/// This style, with `italic` set to the given value.
|
||||||
pub fn with_italic(self, italic: bool) -> Self {
|
pub fn with_italic(self, italic: bool) -> Self {
|
||||||
Self { italic, ..self }
|
Self { italic: Smart::Custom(italic), ..self }
|
||||||
}
|
|
||||||
|
|
||||||
/// This style, with boldness inverted.
|
|
||||||
pub fn with_bold_toggled(self) -> Self {
|
|
||||||
self.with_bold(!self.bold)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This style, with italicness inverted.
|
|
||||||
pub fn with_italic_toggled(self) -> Self {
|
|
||||||
self.with_italic(!self.italic)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The style for subscripts in the current style.
|
/// The style for subscripts in the current style.
|
||||||
@ -377,6 +387,31 @@ impl MathStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The size of elements in a formula.
|
||||||
|
///
|
||||||
|
/// See the TeXbook p. 141.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub enum MathSize {
|
||||||
|
/// Second-level sub- and superscripts.
|
||||||
|
ScriptScript,
|
||||||
|
/// Sub- and superscripts.
|
||||||
|
Script,
|
||||||
|
/// Math in text.
|
||||||
|
Text,
|
||||||
|
/// Math on its own line.
|
||||||
|
Display,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MathSize {
|
||||||
|
pub(super) fn factor(self, ctx: &MathContext) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::Display | Self::Text => 1.0,
|
||||||
|
Self::Script => percent!(ctx, script_percent_scale_down),
|
||||||
|
Self::ScriptScript => percent!(ctx, script_script_percent_scale_down),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A mathematical style variant, as defined by Unicode.
|
/// A mathematical style variant, as defined by Unicode.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum MathVariant {
|
pub enum MathVariant {
|
||||||
@ -401,17 +436,17 @@ impl Default for MathVariant {
|
|||||||
pub(super) fn styled_char(style: MathStyle, c: char) -> char {
|
pub(super) fn styled_char(style: MathStyle, c: char) -> char {
|
||||||
use MathVariant::*;
|
use MathVariant::*;
|
||||||
|
|
||||||
let tuple = (style.variant, style.bold, style.italic);
|
let (base, default_italic) = match c {
|
||||||
let base = match c {
|
'a'..='z' => ('a', true),
|
||||||
'a'..='z' => 'a',
|
'A'..='Z' => ('A', true),
|
||||||
'A'..='Z' => 'A',
|
'α'..='ω' => ('α', false),
|
||||||
'α'..='ω' => 'α',
|
'Α'..='Ω' => ('Α', false),
|
||||||
'Α'..='Ω' => 'Α',
|
'0'..='9' => ('0', false),
|
||||||
'0'..='9' => '0',
|
|
||||||
'-' => return '−',
|
'-' => return '−',
|
||||||
_ => return c,
|
_ => return c,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tuple = (style.variant, style.bold, style.italic.unwrap_or(default_italic));
|
||||||
let start = match c {
|
let start = match c {
|
||||||
// Latin upper.
|
// Latin upper.
|
||||||
'A'..='Z' => match tuple {
|
'A'..='Z' => match tuple {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user