mirror of
https://github.com/typst/typst
synced 2025-06-28 00:03:17 +08:00
Fix sub- and superscript ascent/descent
This commit is contained in:
parent
137d2ab317
commit
6ca6f43703
@ -50,19 +50,14 @@ impl LayoutMath for AttachNode {
|
|||||||
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
let base = ctx.layout_fragment(&self.base)?;
|
let base = ctx.layout_fragment(&self.base)?;
|
||||||
|
|
||||||
let mut top = Frame::new(Size::zero());
|
ctx.style(ctx.style.for_subscript());
|
||||||
if let Some(node) = &self.top {
|
let top = self.top.as_ref().map(|node| ctx.layout_frame(node)).transpose()?;
|
||||||
ctx.style(ctx.style.for_subscript());
|
ctx.unstyle();
|
||||||
top = ctx.layout_frame(node)?;
|
|
||||||
ctx.unstyle();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bottom = Frame::new(Size::zero());
|
ctx.style(ctx.style.for_superscript());
|
||||||
if let Some(node) = &self.bottom {
|
let bottom =
|
||||||
ctx.style(ctx.style.for_superscript());
|
self.bottom.as_ref().map(|node| ctx.layout_frame(node)).transpose()?;
|
||||||
bottom = ctx.layout_frame(node)?;
|
ctx.unstyle();
|
||||||
ctx.unstyle();
|
|
||||||
}
|
|
||||||
|
|
||||||
let render_limits = self.base.is::<LimitsNode>()
|
let render_limits = self.base.is::<LimitsNode>()
|
||||||
|| (!self.base.is::<ScriptsNode>()
|
|| (!self.base.is::<ScriptsNode>()
|
||||||
@ -77,7 +72,7 @@ impl LayoutMath for AttachNode {
|
|||||||
if render_limits {
|
if render_limits {
|
||||||
limits(ctx, base, top, bottom)
|
limits(ctx, base, top, bottom)
|
||||||
} else {
|
} else {
|
||||||
scripts(ctx, base, top, bottom, self.top.is_some() && self.bottom.is_some())
|
scripts(ctx, base, top, bottom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,9 +145,8 @@ impl LayoutMath for LimitsNode {
|
|||||||
fn scripts(
|
fn scripts(
|
||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
base: MathFragment,
|
base: MathFragment,
|
||||||
sup: Frame,
|
sup: Option<Frame>,
|
||||||
sub: Frame,
|
sub: Option<Frame>,
|
||||||
both: bool,
|
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let sup_shift_up = if ctx.style.cramped {
|
let sup_shift_up = if ctx.style.cramped {
|
||||||
scaled!(ctx, superscript_shift_up_cramped)
|
scaled!(ctx, superscript_shift_up_cramped)
|
||||||
@ -168,15 +162,22 @@ fn scripts(
|
|||||||
let sub_drop_min = scaled!(ctx, subscript_baseline_drop_min);
|
let sub_drop_min = scaled!(ctx, subscript_baseline_drop_min);
|
||||||
let space_after = scaled!(ctx, space_after_script);
|
let space_after = scaled!(ctx, space_after_script);
|
||||||
|
|
||||||
let mut shift_up = sup_shift_up
|
let mut shift_up = Abs::zero();
|
||||||
.max(base.ascent() - sup_drop_max)
|
let mut shift_down = Abs::zero();
|
||||||
.max(sup_bottom_min + sup.descent());
|
|
||||||
|
|
||||||
let mut shift_down = sub_shift_down
|
if let Some(sup) = &sup {
|
||||||
.max(base.descent() + sub_drop_min)
|
shift_up = sup_shift_up
|
||||||
.max(sub.ascent() - sub_top_max);
|
.max(base.ascent() - sup_drop_max)
|
||||||
|
.max(sup_bottom_min + sup.descent());
|
||||||
|
}
|
||||||
|
|
||||||
if both {
|
if let Some(sub) = &sub {
|
||||||
|
shift_down = sub_shift_down
|
||||||
|
.max(base.descent() + sub_drop_min)
|
||||||
|
.max(sub.ascent() - sub_top_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (Some(sup), Some(sub)) = (&sup, &sub) {
|
||||||
let sup_bottom = shift_up - sup.descent();
|
let sup_bottom = shift_up - sup.descent();
|
||||||
let sub_top = sub.ascent() - shift_down;
|
let sub_top = sub.ascent() - shift_down;
|
||||||
let gap = sup_bottom - sub_top;
|
let gap = sup_bottom - sub_top;
|
||||||
@ -191,27 +192,48 @@ fn scripts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let italics = base.italics_correction();
|
let italics = base.italics_correction();
|
||||||
let top_delta = match base.class() {
|
let sub_delta = -italics;
|
||||||
|
let sup_delta = match base.class() {
|
||||||
Some(MathClass::Large) => Abs::zero(),
|
Some(MathClass::Large) => Abs::zero(),
|
||||||
_ => italics,
|
_ => italics,
|
||||||
};
|
};
|
||||||
let bottom_delta = -italics;
|
|
||||||
let ascent = shift_up + sup.ascent();
|
let mut width = Abs::zero();
|
||||||
let descent = shift_down + sub.descent();
|
let mut ascent = base.ascent();
|
||||||
let height = ascent + descent;
|
let mut descent = base.descent();
|
||||||
let width = base.width()
|
|
||||||
+ (sup.width() + top_delta).max(sub.width() + bottom_delta)
|
if let Some(sup) = &sup {
|
||||||
+ space_after;
|
ascent.set_max(shift_up + sup.ascent());
|
||||||
|
width.set_max(sup_delta + sup.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sub) = &sub {
|
||||||
|
descent.set_max(shift_down + sub.descent());
|
||||||
|
width.set_max(sub_delta + sub.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
width += base.width() + space_after;
|
||||||
|
|
||||||
let base_pos = Point::with_y(ascent - base.ascent());
|
let base_pos = Point::with_y(ascent - base.ascent());
|
||||||
let sup_pos = Point::with_x(base.width() + top_delta);
|
let base_width = base.width();
|
||||||
let sub_pos = Point::new(base.width() + bottom_delta, height - sub.height());
|
|
||||||
let class = base.class().unwrap_or(MathClass::Normal);
|
let class = base.class().unwrap_or(MathClass::Normal);
|
||||||
|
|
||||||
let mut frame = Frame::new(Size::new(width, height));
|
let mut frame = Frame::new(Size::new(width, ascent + descent));
|
||||||
frame.set_baseline(ascent);
|
frame.set_baseline(ascent);
|
||||||
frame.push_frame(base_pos, base.to_frame(ctx));
|
frame.push_frame(base_pos, base.to_frame(ctx));
|
||||||
frame.push_frame(sup_pos, sup);
|
|
||||||
frame.push_frame(sub_pos, sub);
|
if let Some(sup) = sup {
|
||||||
|
let sup_pos =
|
||||||
|
Point::new(sup_delta + base_width, ascent - shift_up - sup.ascent());
|
||||||
|
frame.push_frame(sup_pos, sup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sub) = sub {
|
||||||
|
let sub_pos =
|
||||||
|
Point::new(sub_delta + base_width, ascent + shift_down - sub.ascent());
|
||||||
|
frame.push_frame(sub_pos, sub);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.push(FrameFragment::new(frame).with_class(class));
|
ctx.push(FrameFragment::new(frame).with_class(class));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -221,31 +243,50 @@ fn scripts(
|
|||||||
fn limits(
|
fn limits(
|
||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
base: MathFragment,
|
base: MathFragment,
|
||||||
top: Frame,
|
top: Option<Frame>,
|
||||||
bottom: Frame,
|
bottom: Option<Frame>,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let upper_gap_min = scaled!(ctx, upper_limit_gap_min);
|
let upper_gap_min = scaled!(ctx, upper_limit_gap_min);
|
||||||
let upper_rise_min = scaled!(ctx, upper_limit_baseline_rise_min);
|
let upper_rise_min = scaled!(ctx, upper_limit_baseline_rise_min);
|
||||||
let lower_gap_min = scaled!(ctx, lower_limit_gap_min);
|
let lower_gap_min = scaled!(ctx, lower_limit_gap_min);
|
||||||
let lower_drop_min = scaled!(ctx, lower_limit_baseline_drop_min);
|
let lower_drop_min = scaled!(ctx, lower_limit_baseline_drop_min);
|
||||||
|
|
||||||
let top_gap = upper_gap_min.max(upper_rise_min - top.descent());
|
let mut base_offset = Abs::zero();
|
||||||
let bottom_gap = lower_gap_min.max(lower_drop_min - bottom.ascent());
|
let mut width = base.width();
|
||||||
|
let mut height = base.height();
|
||||||
|
|
||||||
let delta = base.italics_correction() / 2.0;
|
if let Some(top) = &top {
|
||||||
let width = base.width().max(top.width()).max(bottom.width());
|
let top_gap = upper_gap_min.max(upper_rise_min - top.descent());
|
||||||
let height = top.height() + top_gap + base.height() + bottom_gap + bottom.height();
|
width.set_max(top.width());
|
||||||
let base_pos = Point::new((width - base.width()) / 2.0, top.height() + top_gap);
|
height += top.height() + top_gap;
|
||||||
let sup_pos = Point::with_x((width - top.width()) / 2.0 + delta);
|
base_offset = top_gap + top.height();
|
||||||
let sub_pos =
|
}
|
||||||
Point::new((width - bottom.width()) / 2.0 - delta, height - bottom.height());
|
|
||||||
|
if let Some(bottom) = &bottom {
|
||||||
|
let bottom_gap = lower_gap_min.max(lower_drop_min - bottom.ascent());
|
||||||
|
width.set_max(bottom.width());
|
||||||
|
height += bottom.height() + bottom_gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_pos = Point::new((width - base.width()) / 2.0, base_offset);
|
||||||
let class = base.class().unwrap_or(MathClass::Normal);
|
let class = base.class().unwrap_or(MathClass::Normal);
|
||||||
|
let delta = base.italics_correction() / 2.0;
|
||||||
|
|
||||||
let mut frame = Frame::new(Size::new(width, height));
|
let mut frame = Frame::new(Size::new(width, height));
|
||||||
frame.set_baseline(base_pos.y + base.ascent());
|
frame.set_baseline(base_pos.y + base.ascent());
|
||||||
frame.push_frame(base_pos, base.to_frame(ctx));
|
frame.push_frame(base_pos, base.to_frame(ctx));
|
||||||
frame.push_frame(sub_pos, bottom);
|
|
||||||
frame.push_frame(sup_pos, top);
|
if let Some(top) = top {
|
||||||
|
let top_pos = Point::with_x((width - top.width()) / 2.0 + delta);
|
||||||
|
frame.push_frame(top_pos, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(bottom) = bottom {
|
||||||
|
let bottom_pos =
|
||||||
|
Point::new((width - bottom.width()) / 2.0 - delta, height - bottom.height());
|
||||||
|
frame.push_frame(bottom_pos, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.push(FrameFragment::new(frame).with_class(class));
|
ctx.push(FrameFragment::new(frame).with_class(class));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user