Fix math size resolving (#5596)

This commit is contained in:
Max 2024-12-20 12:35:57 +00:00 committed by GitHub
parent 39706fe42f
commit bb38a01d06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 176 additions and 149 deletions

View File

@ -1,12 +1,9 @@
use typst_library::diag::SourceResult;
use typst_library::foundations::{Packed, StyleChain};
use typst_library::layout::{Em, Frame, Point, Rel, Size};
use typst_library::layout::{Em, Frame, Point, Size};
use typst_library::math::{Accent, AccentElem};
use super::{
scaled_font_size, style_cramped, FrameFragment, GlyphFragment, MathContext,
MathFragment,
};
use super::{style_cramped, FrameFragment, GlyphFragment, MathContext, MathFragment};
/// How much the accent can be shorter than the base.
const ACCENT_SHORT_FALL: Em = Em::new(0.5);
@ -30,11 +27,7 @@ pub fn layout_accent(
let base_class = base.class();
let base_attach = base.accent_attach();
let width = elem
.size(styles)
.unwrap_or(Rel::one())
.at(scaled_font_size(ctx, styles))
.relative_to(base.width());
let width = elem.size(styles).relative_to(base.width());
let Accent(c) = elem.accent();
let mut glyph = GlyphFragment::new(ctx, styles, *c, elem.span());
@ -75,7 +68,7 @@ pub fn layout_accent(
frame.push_frame(accent_pos, accent);
frame.push_frame(base_pos, base.into_frame());
ctx.push(
FrameFragment::new(ctx, styles, frame)
FrameFragment::new(styles, frame)
.with_class(base_class)
.with_base_ascent(base_ascent)
.with_italics_correction(base_italics_correction)

View File

@ -1,6 +1,6 @@
use typst_library::diag::SourceResult;
use typst_library::foundations::{Packed, Smart, StyleChain};
use typst_library::layout::{Abs, Axis, Corner, Frame, Length, Point, Rel, Size};
use typst_library::foundations::{Packed, StyleChain};
use typst_library::layout::{Abs, Axis, Corner, Frame, Point, Rel, Size};
use typst_library::math::{
AttachElem, EquationElem, LimitsElem, PrimesElem, ScriptsElem, StretchElem,
};
@ -121,7 +121,7 @@ pub fn layout_primes(
prime.clone(),
)
}
ctx.push(FrameFragment::new(ctx, styles, frame).with_text_like(true));
ctx.push(FrameFragment::new(styles, frame).with_text_like(true));
}
}
Ok(())
@ -154,11 +154,8 @@ pub fn layout_limits(
Ok(())
}
/// Get the size to stretch the base to, if the attach argument is true.
fn stretch_size(
styles: StyleChain,
elem: &Packed<AttachElem>,
) -> Option<Smart<Rel<Length>>> {
/// Get the size to stretch the base to.
fn stretch_size(styles: StyleChain, elem: &Packed<AttachElem>) -> Option<Rel<Abs>> {
// Extract from an EquationElem.
let mut base = elem.base();
while let Some(equation) = base.to_packed::<EquationElem>() {
@ -277,7 +274,7 @@ fn layout_attachments(
layout!(b, b_x, b_y); // lower-limit
// Done! Note that we retain the class of the base.
ctx.push(FrameFragment::new(ctx, styles, frame).with_class(base_class));
ctx.push(FrameFragment::new(styles, frame).with_class(base_class));
Ok(())
}

View File

@ -7,7 +7,7 @@ use typst_library::text::TextElem;
use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span;
use super::{scaled_font_size, FrameFragment, MathContext};
use super::{FrameFragment, MathContext};
/// Lays out a [`CancelElem`].
#[typst_macros::time(name = "math.cancel", span = elem.span())]
@ -27,7 +27,7 @@ pub fn layout_cancel(
let mut body = body.into_frame();
let body_size = body.size();
let span = elem.span();
let length = elem.length(styles).at(scaled_font_size(ctx, styles));
let length = elem.length(styles);
let stroke = elem.stroke(styles).unwrap_or(FixedStroke {
paint: TextElem::fill_in(styles).as_decoration(),
@ -63,7 +63,7 @@ pub fn layout_cancel(
}
ctx.push(
FrameFragment::new(ctx, styles, body)
FrameFragment::new(styles, body)
.with_class(body_class)
.with_italics_correction(body_italics)
.with_accent_attach(body_attach)

View File

@ -1,5 +1,5 @@
use typst_library::diag::SourceResult;
use typst_library::foundations::{Content, Packed, StyleChain};
use typst_library::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::layout::{Em, Frame, FrameItem, Point, Size};
use typst_library::math::{BinomElem, FracElem};
use typst_library::text::TextElem;
@ -7,8 +7,8 @@ use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span;
use super::{
scaled_font_size, style_for_denominator, style_for_numerator, FrameFragment,
GlyphFragment, MathContext, DELIM_SHORT_FALL,
style_for_denominator, style_for_numerator, FrameFragment, GlyphFragment,
MathContext, DELIM_SHORT_FALL,
};
const FRAC_AROUND: Em = Em::new(0.1);
@ -49,8 +49,7 @@ fn layout_frac_like(
binom: bool,
span: Span,
) -> SourceResult<()> {
let font_size = scaled_font_size(ctx, styles);
let short_fall = DELIM_SHORT_FALL.at(font_size);
let short_fall = DELIM_SHORT_FALL.resolve(styles);
let axis = scaled!(ctx, styles, axis_height);
let thickness = scaled!(ctx, styles, fraction_rule_thickness);
let shift_up = scaled!(
@ -86,7 +85,7 @@ fn layout_frac_like(
styles.chain(&denom_style),
)?;
let around = FRAC_AROUND.at(font_size);
let around = FRAC_AROUND.resolve(styles);
let num_gap = (shift_up - (axis + thickness / 2.0) - num.descent()).max(num_min);
let denom_gap =
(shift_down + (axis - thickness / 2.0) - denom.ascent()).max(denom_min);
@ -111,7 +110,7 @@ fn layout_frac_like(
.stretch_vertical(ctx, height, short_fall);
left.center_on_axis(ctx);
ctx.push(left);
ctx.push(FrameFragment::new(ctx, styles, frame));
ctx.push(FrameFragment::new(styles, frame));
let mut right = GlyphFragment::new(ctx, styles, ')', span)
.stretch_vertical(ctx, height, short_fall);
right.center_on_axis(ctx);
@ -129,7 +128,7 @@ fn layout_frac_like(
span,
),
);
ctx.push(FrameFragment::new(ctx, styles, frame));
ctx.push(FrameFragment::new(styles, frame));
}
Ok(())

View File

@ -17,7 +17,7 @@ use typst_library::visualize::Paint;
use typst_syntax::Span;
use unicode_math_class::MathClass;
use super::{scaled_font_size, stretch_glyph, MathContext, Scaled};
use super::{stretch_glyph, MathContext, Scaled};
#[derive(Debug, Clone)]
pub enum MathFragment {
@ -292,7 +292,7 @@ impl GlyphFragment {
region: TextElem::region_in(styles),
fill: TextElem::fill_in(styles).as_decoration(),
shift: TextElem::baseline_in(styles),
font_size: scaled_font_size(ctx, styles),
font_size: TextElem::size_in(styles),
math_size: EquationElem::size_in(styles),
width: Abs::zero(),
ascent: Abs::zero(),
@ -512,12 +512,12 @@ pub struct FrameFragment {
}
impl FrameFragment {
pub fn new(ctx: &MathContext, styles: StyleChain, frame: Frame) -> Self {
pub fn new(styles: StyleChain, frame: Frame) -> Self {
let base_ascent = frame.ascent();
let accent_attach = frame.width() / 2.0;
Self {
frame: frame.post_processed(styles),
font_size: scaled_font_size(ctx, styles),
font_size: TextElem::size_in(styles),
class: EquationElem::class_in(styles).unwrap_or(MathClass::Normal),
math_size: EquationElem::size_in(styles),
limits: Limits::Never,

View File

@ -1,6 +1,6 @@
use typst_library::diag::SourceResult;
use typst_library::foundations::{Packed, Smart, StyleChain};
use typst_library::layout::{Abs, Axis, Length, Rel};
use typst_library::foundations::{Packed, StyleChain};
use typst_library::layout::{Abs, Axis, Rel};
use typst_library::math::{EquationElem, LrElem, MidElem};
use unicode_math_class::MathClass;
@ -22,7 +22,7 @@ pub fn layout_lr(
// Extract implicit LrElem.
if let Some(lr) = body.to_packed::<LrElem>() {
if lr.size(styles).is_auto() {
if lr.size(styles).is_one() {
body = lr.body();
}
}
@ -128,7 +128,7 @@ fn scale(
styles: StyleChain,
fragment: &mut MathFragment,
relative_to: Abs,
height: Smart<Rel<Length>>,
height: Rel<Abs>,
apply: Option<MathClass>,
) {
if matches!(

View File

@ -1,5 +1,5 @@
use typst_library::diag::{bail, SourceResult};
use typst_library::foundations::{Content, Packed, StyleChain};
use typst_library::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::layout::{
Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Point, Ratio, Rel, Size,
};
@ -9,9 +9,8 @@ use typst_library::visualize::{FillRule, FixedStroke, Geometry, LineCap, Shape};
use typst_syntax::Span;
use super::{
alignments, delimiter_alignment, scaled_font_size, stack, style_for_denominator,
AlignmentResult, FrameFragment, GlyphFragment, LeftRightAlternator, MathContext,
Scaled, DELIM_SHORT_FALL,
alignments, delimiter_alignment, stack, style_for_denominator, AlignmentResult,
FrameFragment, GlyphFragment, LeftRightAlternator, MathContext, DELIM_SHORT_FALL,
};
const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
@ -30,7 +29,7 @@ pub fn layout_vec(
styles,
elem.children(),
elem.align(styles),
elem.gap(styles).at(scaled_font_size(ctx, styles)),
elem.gap(styles),
LeftRightAlternator::Right,
)?;
@ -73,9 +72,6 @@ pub fn layout_mat(
}
}
let font_size = scaled_font_size(ctx, styles);
let column_gap = elem.column_gap(styles).at(font_size);
let row_gap = elem.row_gap(styles).at(font_size);
let delim = elem.delim(styles);
let frame = layout_mat_body(
ctx,
@ -83,7 +79,7 @@ pub fn layout_mat(
rows,
elem.align(styles),
augment,
Axes::new(column_gap, row_gap),
Axes::new(elem.column_gap(styles), elem.row_gap(styles)),
elem.span(),
)?;
@ -103,7 +99,7 @@ pub fn layout_cases(
styles,
elem.children(),
FixedAlignment::Start,
elem.gap(styles).at(scaled_font_size(ctx, styles)),
elem.gap(styles),
LeftRightAlternator::None,
)?;
@ -162,8 +158,7 @@ fn layout_mat_body(
// with font size to ensure that augmentation lines
// look correct by default at all matrix sizes.
// The line cap is also set to square because it looks more "correct".
let font_size = scaled_font_size(ctx, styles);
let default_stroke_thickness = DEFAULT_STROKE_THICKNESS.at(font_size);
let default_stroke_thickness = DEFAULT_STROKE_THICKNESS.resolve(styles);
let default_stroke = FixedStroke {
thickness: default_stroke_thickness,
paint: TextElem::fill_in(styles).as_decoration(),
@ -308,9 +303,8 @@ fn layout_delimiters(
right: Option<char>,
span: Span,
) -> SourceResult<()> {
let font_size = scaled_font_size(ctx, styles);
let short_fall = DELIM_SHORT_FALL.at(font_size);
let axis = ctx.constants.axis_height().scaled(ctx, font_size);
let short_fall = DELIM_SHORT_FALL.resolve(styles);
let axis = scaled!(ctx, styles, axis_height);
let height = frame.height();
let target = height + VERTICAL_PADDING.of(height);
frame.set_baseline(height / 2.0 + axis);
@ -322,7 +316,7 @@ fn layout_delimiters(
ctx.push(left);
}
ctx.push(FrameFragment::new(ctx, styles, frame));
ctx.push(FrameFragment::new(styles, frame));
if let Some(right) = right {
let mut right = GlyphFragment::new(ctx, styles, right, span)

View File

@ -28,8 +28,7 @@ use typst_library::math::*;
use typst_library::model::ParElem;
use typst_library::routines::{Arenas, RealizationKind};
use typst_library::text::{
families, features, variant, Font, LinebreakElem, SpaceElem, TextEdgeBounds,
TextElem, TextSize,
families, features, variant, Font, LinebreakElem, SpaceElem, TextEdgeBounds, TextElem,
};
use typst_library::World;
use typst_syntax::Span;
@ -58,12 +57,16 @@ pub fn layout_equation_inline(
let mut locator = locator.split();
let mut ctx = MathContext::new(engine, &mut locator, styles, region, &font);
let scale_style = style_for_script_scale(&ctx);
let styles = styles.chain(&scale_style);
let run = ctx.layout_into_run(&elem.body, styles)?;
let mut items = if run.row_count() == 1 {
run.into_par_items()
} else {
vec![InlineItem::Frame(run.into_fragment(&ctx, styles).into_frame())]
vec![InlineItem::Frame(run.into_fragment(styles).into_frame())]
};
// An empty equation should have a height, so we still create a frame
@ -75,13 +78,12 @@ pub fn layout_equation_inline(
for item in &mut items {
let InlineItem::Frame(frame) = item else { continue };
let font_size = scaled_font_size(&ctx, styles);
let slack = ParElem::leading_in(styles) * 0.7;
let (t, b) = font.edges(
TextElem::top_edge_in(styles),
TextElem::bottom_edge_in(styles),
font_size,
TextElem::size_in(styles),
TextEdgeBounds::Frame(frame),
);
@ -110,9 +112,13 @@ pub fn layout_equation_block(
let mut locator = locator.split();
let mut ctx = MathContext::new(engine, &mut locator, styles, regions.base(), &font);
let scale_style = style_for_script_scale(&ctx);
let styles = styles.chain(&scale_style);
let full_equation_builder = ctx
.layout_into_run(&elem.body, styles)?
.multiline_frame_builder(&ctx, styles);
.multiline_frame_builder(styles);
let width = full_equation_builder.size.x;
let equation_builders = if BlockElem::breakable_in(styles) {
@ -469,7 +475,7 @@ impl<'a, 'v, 'e> MathContext<'a, 'v, 'e> {
elem: &Content,
styles: StyleChain,
) -> SourceResult<MathFragment> {
Ok(self.layout_into_run(elem, styles)?.into_fragment(self, styles))
Ok(self.layout_into_run(elem, styles)?.into_fragment(styles))
}
/// Layout the given element and return the result as a [`Frame`].
@ -502,7 +508,7 @@ impl<'a, 'v, 'e> MathContext<'a, 'v, 'e> {
// Hack because the font is fixed in math.
if styles != outer && TextElem::font_in(styles) != TextElem::font_in(outer) {
let frame = layout_external(elem, self, styles)?;
self.push(FrameFragment::new(self, styles, frame).with_spaced(true));
self.push(FrameFragment::new(styles, frame).with_spaced(true));
continue;
}
@ -522,8 +528,7 @@ fn layout_realized(
if let Some(elem) = elem.to_packed::<TagElem>() {
ctx.push(MathFragment::Tag(elem.tag.clone()));
} else if elem.is::<SpaceElem>() {
let font_size = scaled_font_size(ctx, styles);
ctx.push(MathFragment::Space(ctx.space_width.at(font_size)));
ctx.push(MathFragment::Space(ctx.space_width.resolve(styles)));
} else if elem.is::<LinebreakElem>() {
ctx.push(MathFragment::Linebreak);
} else if let Some(elem) = elem.to_packed::<HElem>() {
@ -595,7 +600,7 @@ fn layout_realized(
frame.set_baseline(frame.height() / 2.0 + axis);
}
ctx.push(
FrameFragment::new(ctx, styles, frame)
FrameFragment::new(styles, frame)
.with_spaced(true)
.with_ignorant(elem.is::<PlaceElem>()),
);
@ -610,15 +615,14 @@ fn layout_box(
ctx: &mut MathContext,
styles: StyleChain,
) -> SourceResult<()> {
let local = TextElem::set_size(TextSize(scaled_font_size(ctx, styles).into())).wrap();
let frame = (ctx.engine.routines.layout_box)(
elem,
ctx.engine,
ctx.locator.next(&elem.span()),
styles.chain(&local),
styles,
ctx.region.size,
)?;
ctx.push(FrameFragment::new(ctx, styles, frame).with_spaced(true));
ctx.push(FrameFragment::new(styles, frame).with_spaced(true));
Ok(())
}
@ -630,10 +634,7 @@ fn layout_h(
) -> SourceResult<()> {
if let Spacing::Rel(rel) = elem.amount() {
if rel.rel.is_zero() {
ctx.push(MathFragment::Spacing(
rel.abs.at(scaled_font_size(ctx, styles)),
elem.weak(styles),
));
ctx.push(MathFragment::Spacing(rel.abs.resolve(styles), elem.weak(styles)));
}
}
Ok(())
@ -668,7 +669,7 @@ fn layout_op(
let text_like = fragment.is_text_like();
ctx.push(
FrameFragment::new(ctx, styles, fragment.into_frame())
FrameFragment::new(styles, fragment.into_frame())
.with_class(MathClass::Large)
.with_italics_correction(italics)
.with_accent_attach(accent_attach)
@ -688,12 +689,11 @@ fn layout_external(
ctx: &mut MathContext,
styles: StyleChain,
) -> SourceResult<Frame> {
let local = TextElem::set_size(TextSize(scaled_font_size(ctx, styles).into())).wrap();
(ctx.engine.routines.layout_frame)(
ctx.engine,
content,
ctx.locator.next(&content.span()),
styles.chain(&local),
styles,
ctx.region,
)
}

View File

@ -38,7 +38,7 @@ pub fn layout_root(
let styles = styles.chain(&cramped);
let run = ctx.layout_into_run(radicand, styles)?;
let multiline = run.is_multiline();
let mut radicand = run.into_fragment(ctx, styles).into_frame();
let mut radicand = run.into_fragment(styles).into_frame();
if multiline {
// Align the frame center line with the math axis.
radicand.set_baseline(
@ -120,7 +120,7 @@ pub fn layout_root(
);
frame.push_frame(radicand_pos, radicand);
ctx.push(FrameFragment::new(ctx, styles, frame));
ctx.push(FrameFragment::new(styles, frame));
Ok(())
}

View File

@ -6,7 +6,7 @@ use typst_library::math::{EquationElem, MathSize, MEDIUM, THICK, THIN};
use typst_library::model::ParElem;
use unicode_math_class::MathClass;
use super::{alignments, scaled_font_size, FrameFragment, MathContext, MathFragment};
use super::{alignments, FrameFragment, MathFragment};
const TIGHT_LEADING: Em = Em::new(0.25);
@ -161,15 +161,15 @@ impl MathRun {
}
}
pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame {
pub fn into_frame(self, styles: StyleChain) -> Frame {
if !self.is_multiline() {
self.into_line_frame(&[], LeftRightAlternator::Right)
} else {
self.multiline_frame_builder(ctx, styles).build()
self.multiline_frame_builder(styles).build()
}
}
pub fn into_fragment(self, ctx: &MathContext, styles: StyleChain) -> MathFragment {
pub fn into_fragment(self, styles: StyleChain) -> MathFragment {
if self.0.len() == 1 {
return self.0.into_iter().next().unwrap();
}
@ -181,7 +181,7 @@ impl MathRun {
.filter(|e| e.math_size().is_some())
.all(|e| e.is_text_like());
FrameFragment::new(ctx, styles, self.into_frame(ctx, styles))
FrameFragment::new(styles, self.into_frame(styles))
.with_text_like(text_like)
.into()
}
@ -189,11 +189,7 @@ impl MathRun {
/// Returns a builder that lays out the [`MathFragment`]s into a possibly
/// multi-row [`Frame`]. The rows are aligned using the same set of alignment
/// points computed from them as a whole.
pub fn multiline_frame_builder(
self,
ctx: &MathContext,
styles: StyleChain,
) -> MathRunFrameBuilder {
pub fn multiline_frame_builder(self, styles: StyleChain) -> MathRunFrameBuilder {
let rows: Vec<_> = self.rows();
let row_count = rows.len();
let alignments = alignments(&rows);
@ -201,8 +197,7 @@ impl MathRun {
let leading = if EquationElem::size_in(styles) >= MathSize::Text {
ParElem::leading_in(styles)
} else {
let font_size = scaled_font_size(ctx, styles);
TIGHT_LEADING.at(font_size)
TIGHT_LEADING.resolve(styles)
};
let align = AlignElem::alignment_in(styles).resolve(styles).x;

View File

@ -2,7 +2,6 @@ use ttf_parser::math::MathValue;
use typst_library::foundations::{Style, StyleChain};
use typst_library::layout::{Abs, Em, FixedAlignment, Frame, Point, Size, VAlignment};
use typst_library::math::{EquationElem, MathSize};
use typst_library::text::TextElem;
use typst_utils::LazyHash;
use super::{LeftRightAlternator, MathContext, MathFragment, MathRun};
@ -18,7 +17,7 @@ macro_rules! scaled {
$crate::math::Scaled::scaled(
$ctx.constants.$name(),
$ctx,
$crate::math::scaled_font_size($ctx, $styles),
typst_library::text::TextElem::size_in($styles),
)
};
}
@ -55,16 +54,6 @@ impl Scaled for MathValue<'_> {
}
}
/// Get the font size scaled with the `MathSize`.
pub fn scaled_font_size(ctx: &MathContext, styles: StyleChain) -> Abs {
let factor = match EquationElem::size_in(styles) {
MathSize::Display | MathSize::Text => 1.0,
MathSize::Script => percent!(ctx, script_percent_scale_down),
MathSize::ScriptScript => percent!(ctx, script_script_percent_scale_down),
};
factor * TextElem::size_in(styles)
}
/// Styles something as cramped.
pub fn style_cramped() -> LazyHash<Style> {
EquationElem::set_cramped(true).wrap()
@ -99,6 +88,15 @@ pub fn style_for_denominator(styles: StyleChain) -> [LazyHash<Style>; 2] {
[style_for_numerator(styles), EquationElem::set_cramped(true).wrap()]
}
/// Styles to add font constants to the style chain.
pub fn style_for_script_scale(ctx: &MathContext) -> LazyHash<Style> {
EquationElem::set_script_scale((
ctx.constants.script_percent_scale_down(),
ctx.constants.script_script_percent_scale_down(),
))
.wrap()
}
/// How a delimieter should be aligned when scaling.
pub fn delimiter_alignment(delimiter: char) -> VAlignment {
match delimiter {

View File

@ -1,14 +1,14 @@
use ttf_parser::math::{GlyphAssembly, GlyphConstruction, GlyphPart};
use ttf_parser::LazyArray16;
use typst_library::diag::{warning, SourceResult};
use typst_library::foundations::{Packed, Smart, StyleChain};
use typst_library::layout::{Abs, Axis, Frame, Length, Point, Rel, Size};
use typst_library::foundations::{Packed, StyleChain};
use typst_library::layout::{Abs, Axis, Frame, Point, Rel, Size};
use typst_library::math::StretchElem;
use typst_utils::Get;
use super::{
delimiter_alignment, scaled_font_size, GlyphFragment, MathContext, MathFragment,
Scaled, VariantFragment,
delimiter_alignment, GlyphFragment, MathContext, MathFragment, Scaled,
VariantFragment,
};
/// Maximum number of times extenders can be repeated.
@ -42,7 +42,7 @@ pub fn stretch_fragment(
fragment: &mut MathFragment,
axis: Option<Axis>,
relative_to: Option<Abs>,
stretch: Smart<Rel<Length>>,
stretch: Rel<Abs>,
short_fall: Abs,
) {
let glyph = match fragment {
@ -66,10 +66,7 @@ pub fn stretch_fragment(
let mut variant = stretch_glyph(
ctx,
glyph,
stretch
.unwrap_or(Rel::one())
.at(scaled_font_size(ctx, styles))
.relative_to(relative_to_size),
stretch.relative_to(relative_to_size),
short_fall,
axis,
);

View File

@ -6,15 +6,13 @@ use typst_library::foundations::{Packed, StyleChain, StyleVec};
use typst_library::layout::{Abs, Size};
use typst_library::math::{EquationElem, MathSize, MathVariant};
use typst_library::text::{
BottomEdge, BottomEdgeMetric, TextElem, TextSize, TopEdge, TopEdgeMetric,
BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric,
};
use typst_syntax::{is_newline, Span};
use unicode_math_class::MathClass;
use unicode_segmentation::UnicodeSegmentation;
use super::{
scaled_font_size, FrameFragment, GlyphFragment, MathContext, MathFragment, MathRun,
};
use super::{FrameFragment, GlyphFragment, MathContext, MathFragment, MathRun};
/// Lays out a [`TextElem`].
pub fn layout_text(
@ -70,13 +68,12 @@ pub fn layout_text(
let c = styled_char(styles, c, false);
fragments.push(GlyphFragment::new(ctx, styles, c, span).into());
}
let frame = MathRun::new(fragments).into_frame(ctx, styles);
FrameFragment::new(ctx, styles, frame).with_text_like(true).into()
let frame = MathRun::new(fragments).into_frame(styles);
FrameFragment::new(styles, frame).with_text_like(true).into()
} else {
let local = [
TextElem::set_top_edge(TopEdge::Metric(TopEdgeMetric::Bounds)),
TextElem::set_bottom_edge(BottomEdge::Metric(BottomEdgeMetric::Bounds)),
TextElem::set_size(TextSize(scaled_font_size(ctx, styles).into())),
]
.map(|p| p.wrap());
@ -94,10 +91,10 @@ pub fn layout_text(
fragments.push(layout_complex_text(piece, ctx, span, styles)?.into());
}
}
let mut frame = MathRun::new(fragments).into_frame(ctx, styles);
let mut frame = MathRun::new(fragments).into_frame(styles);
let axis = scaled!(ctx, styles, axis_height);
frame.set_baseline(frame.height() / 2.0 + axis);
FrameFragment::new(ctx, styles, frame).into()
FrameFragment::new(styles, frame).into()
} else {
layout_complex_text(&text, ctx, span, styles)?.into()
}
@ -131,7 +128,7 @@ fn layout_complex_text(
)?
.into_frame();
Ok(FrameFragment::new(ctx, styles, frame)
Ok(FrameFragment::new(styles, frame)
.with_class(MathClass::Alphabetic)
.with_text_like(true)
.with_spaced(spaced))

View File

@ -1,5 +1,5 @@
use typst_library::diag::SourceResult;
use typst_library::foundations::{Content, Packed, StyleChain};
use typst_library::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
use typst_library::math::{
OverbraceElem, OverbracketElem, OverlineElem, OverparenElem, OvershellElem,
@ -10,8 +10,8 @@ use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span;
use super::{
scaled_font_size, stack, style_cramped, style_for_subscript, style_for_superscript,
FrameFragment, GlyphFragment, LeftRightAlternator, MathContext, MathRun,
stack, style_cramped, style_for_subscript, style_for_superscript, FrameFragment,
GlyphFragment, LeftRightAlternator, MathContext, MathRun,
};
const BRACE_GAP: Em = Em::new(0.25);
@ -260,7 +260,7 @@ fn layout_underoverline(
);
ctx.push(
FrameFragment::new(ctx, styles, frame)
FrameFragment::new(styles, frame)
.with_class(content_class)
.with_text_like(content_is_text_like)
.with_italics_correction(content_italics_correction),
@ -281,11 +281,10 @@ fn layout_underoverspreader(
position: Position,
span: Span,
) -> SourceResult<()> {
let font_size = scaled_font_size(ctx, styles);
let gap = gap.at(font_size);
let gap = gap.resolve(styles);
let body = ctx.layout_into_run(body, styles)?;
let body_class = body.class();
let body = body.into_fragment(ctx, styles);
let body = body.into_fragment(styles);
let glyph = GlyphFragment::new(ctx, styles, c, span);
let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero());
@ -321,7 +320,7 @@ fn layout_underoverspreader(
LeftRightAlternator::Right,
None,
);
ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class));
ctx.push(FrameFragment::new(styles, frame).with_class(body_class));
Ok(())
}

View File

@ -86,12 +86,6 @@ impl Rel<Length> {
None
}
}
/// Convert to a relative length with the absolute part resolved at the
/// given font size.
pub fn at(self, font_size: Abs) -> Rel<Abs> {
self.map(|abs| abs.at(font_size))
}
}
impl<T: Numeric + Debug> Debug for Rel<T> {

View File

@ -1,5 +1,5 @@
use crate::diag::bail;
use crate::foundations::{cast, elem, func, Content, NativeElement, Smart, Value};
use crate::foundations::{cast, elem, func, Content, NativeElement, Value};
use crate::layout::{Length, Rel};
use crate::math::Mathy;
use crate::text::TextElem;
@ -52,7 +52,9 @@ pub struct AccentElem {
pub accent: Accent,
/// The size of the accent, relative to the width of the base.
pub size: Smart<Rel<Length>>,
#[resolve]
#[default(Rel::one())]
pub size: Rel<Length>,
}
/// An accent character.
@ -101,7 +103,7 @@ macro_rules! accents {
base: Content,
/// The size of the accent, relative to the width of the base.
#[named]
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
) -> Content {
let mut accent = AccentElem::new(base, Accent::new($primary));
if let Some(size) = size {

View File

@ -1,4 +1,4 @@
use crate::foundations::{elem, Content, Packed, Smart};
use crate::foundations::{elem, Content, Packed};
use crate::layout::{Length, Rel};
use crate::math::{EquationElem, Mathy};
@ -152,5 +152,7 @@ pub struct StretchElem {
/// The size to stretch to, relative to the maximum size of the glyph and
/// its attachments.
pub size: Smart<Rel<Length>>,
#[resolve]
#[default(Rel::one())]
pub size: Rel<Length>,
}

View File

@ -29,6 +29,7 @@ pub struct CancelElem {
/// $ a + cancel(x, length: #200%)
/// - cancel(x, length: #200%) $
/// ```
#[resolve]
#[default(Rel::new(Ratio::one(), Abs::pt(3.0).into()))]
pub length: Rel<Length>,

View File

@ -135,6 +135,13 @@ pub struct EquationElem {
#[internal]
#[ghost]
pub class: Option<MathClass>,
/// Values of `scriptPercentScaleDown` and `scriptScriptPercentScaleDown`
/// respectively in the current font's MathConstants table.
#[internal]
#[default((70, 50))]
#[ghost]
pub script_scale: (i16, i16),
}
impl Synthesize for Packed<EquationElem> {

View File

@ -1,4 +1,4 @@
use crate::foundations::{elem, func, Content, NativeElement, Smart};
use crate::foundations::{elem, func, Content, NativeElement};
use crate::layout::{Length, Rel};
use crate::math::Mathy;
use crate::text::TextElem;
@ -10,7 +10,9 @@ use crate::text::TextElem;
#[elem(title = "Left/Right", Mathy)]
pub struct LrElem {
/// The size of the brackets, relative to the height of the wrapped content.
pub size: Smart<Rel<Length>>,
#[resolve]
#[default(Rel::one())]
pub size: Rel<Length>,
/// The delimited content, including the delimiters.
#[required]
@ -44,7 +46,7 @@ pub struct MidElem {
pub fn floor(
/// The size of the brackets, relative to the height of the wrapped content.
#[named]
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
/// The expression to floor.
body: Content,
) -> Content {
@ -60,7 +62,7 @@ pub fn floor(
pub fn ceil(
/// The size of the brackets, relative to the height of the wrapped content.
#[named]
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
/// The expression to ceil.
body: Content,
) -> Content {
@ -76,7 +78,7 @@ pub fn ceil(
pub fn round(
/// The size of the brackets, relative to the height of the wrapped content.
#[named]
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
/// The expression to round.
body: Content,
) -> Content {
@ -92,7 +94,7 @@ pub fn round(
pub fn abs(
/// The size of the brackets, relative to the height of the wrapped content.
#[named]
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
/// The expression to take the absolute value of.
body: Content,
) -> Content {
@ -108,7 +110,7 @@ pub fn abs(
pub fn norm(
/// The size of the brackets, relative to the height of the wrapped content.
#[named]
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
/// The expression to take the norm of.
body: Content,
) -> Content {
@ -119,7 +121,7 @@ fn delimited(
body: Content,
left: char,
right: char,
size: Option<Smart<Rel<Length>>>,
size: Option<Rel<Length>>,
) -> Content {
let span = body.span();
let mut elem = LrElem::new(Content::sequence([

View File

@ -56,6 +56,7 @@ pub struct VecElem {
/// #set math.vec(gap: 1em)
/// $ vec(1, 2) $
/// ```
#[resolve]
#[default(DEFAULT_ROW_GAP.into())]
pub gap: Rel<Length>,
@ -161,6 +162,7 @@ pub struct MatElem {
/// #set math.mat(row-gap: 1em)
/// $ mat(1, 2; 3, 4) $
/// ```
#[resolve]
#[parse(
let gap = args.named("gap")?;
args.named("row-gap")?.or(gap)
@ -174,6 +176,7 @@ pub struct MatElem {
/// #set math.mat(column-gap: 1em)
/// $ mat(1, 2; 3, 4) $
/// ```
#[resolve]
#[parse(args.named("column-gap")?.or(gap))]
#[default(DEFAULT_COL_GAP.into())]
pub column_gap: Rel<Length>,
@ -256,6 +259,7 @@ pub struct CasesElem {
/// #set math.cases(gap: 1em)
/// $ x = cases(1, 2) $
/// ```
#[resolve]
#[default(DEFAULT_ROW_GAP.into())]
pub gap: Rel<Length>,

View File

@ -50,6 +50,7 @@ use crate::foundations::{
Resolve, Scope, Set, Smart, StyleChain,
};
use crate::layout::{Abs, Axis, Dir, Em, Length, Ratio, Rel};
use crate::math::{EquationElem, MathSize};
use crate::model::ParElem;
use crate::visualize::{Color, Paint, RelativeTo, Stroke};
use crate::World;
@ -981,7 +982,14 @@ impl Resolve for TextSize {
type Output = Abs;
fn resolve(self, styles: StyleChain) -> Self::Output {
self.0.resolve(styles)
let factor = match EquationElem::size_in(styles) {
MathSize::Display | MathSize::Text => 1.0,
MathSize::Script => EquationElem::script_scale_in(styles).0 as f64 / 100.0,
MathSize::ScriptScript => {
EquationElem::script_scale_in(styles).1 as f64 / 100.0
}
};
factor * self.0.resolve(styles)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -114,3 +114,41 @@ Inline $2 baz(x,y,baz(u, v))$.
$ 2 foo(alpha, (M+foo(a, b))) $
$ 2 bar(alpha, (M+foo(a, b))) $
$ 2 baz(x,y,baz(u, v)) $
--- math-size-resolve ---
#let length = context repr(measure("--").width)
$ a length a ^ length $
--- math-size-arbitrary-content ---
// Test sizing of both relative and absolute non math content in math sizes.
#let stuff = square(inset: 0pt)[hello]
#let square = square(size: 5pt)
$ stuff sum^stuff_square square $
--- math-size-math-content-1 ---
// Nested math content has styles overwritten by the inner equation.
// Ideally the widths would match the actual length of the arrows.
#let arrow = $stretch(->)^"much text"$
$ arrow A^arrow A^A^arrow $
#let width = context measure(arrow).width
$ width A^width A^A^width $
--- math-size-math-content-2 ---
// Nested math content has styles overwritten by the inner equation.
// Ideally the heights would match the actual height of the sums.
#let sum = $sum^2$
#let height(x) = context measure(x).height
$sum = height(sum) $
$ sum != height(sum) $
--- math-size-math-content-3 ---
// Sum doesn't get wrapped in math as it is a single expr.
// Ideally the height would match the actual height of the sum.
#let height(x) = context measure(x).height
$ sum != height(sum) $
--- math-text-size ---
// Values retrieved from function are not resolved at the moment.
// Ideally the left size would match the right size.
#let size = context [#text.size.to-absolute() #1em.to-absolute()]
$ size x^size x^x^size $