mirror of
https://github.com/typst/typst
synced 2025-08-23 19:24:14 +08:00
Merge ce746528b38e1a2ab9c1ee79b23d5b399ffe75d3 into e632bffc2ed4c005e5e989b527a05e87f077a8a0
This commit is contained in:
commit
f69b5bb27a
@ -201,9 +201,12 @@ pub fn collect<'a>(
|
|||||||
|
|
||||||
for item in elem.layout(engine, locator.next(&elem.span()), styles, region)? {
|
for item in elem.layout(engine, locator.next(&elem.span()), styles, region)? {
|
||||||
match item {
|
match item {
|
||||||
InlineItem::Space(space, weak) => {
|
InlineItem::Absolute(space, weak) => {
|
||||||
collector.push_item(Item::Absolute(space, weak));
|
collector.push_item(Item::Absolute(space, weak));
|
||||||
}
|
}
|
||||||
|
InlineItem::Fractional(fr) => {
|
||||||
|
collector.push_item(Item::Fractional(fr, None));
|
||||||
|
}
|
||||||
InlineItem::Frame(mut frame) => {
|
InlineItem::Frame(mut frame) => {
|
||||||
frame.modify(&FrameModifiers::get_in(styles));
|
frame.modify(&FrameModifiers::get_in(styles));
|
||||||
apply_baseline_shift(&mut frame, styles);
|
apply_baseline_shift(&mut frame, styles);
|
||||||
|
@ -7,7 +7,7 @@ use ttf_parser::{GlyphId, Rect};
|
|||||||
use typst_library::foundations::StyleChain;
|
use typst_library::foundations::StyleChain;
|
||||||
use typst_library::introspection::Tag;
|
use typst_library::introspection::Tag;
|
||||||
use typst_library::layout::{
|
use typst_library::layout::{
|
||||||
Abs, Axis, Corner, Em, Frame, FrameItem, Point, Size, VAlignment,
|
Abs, Axis, Corner, Em, Fr, Frame, FrameItem, Point, Size, VAlignment,
|
||||||
};
|
};
|
||||||
use typst_library::math::{EquationElem, MathSize};
|
use typst_library::math::{EquationElem, MathSize};
|
||||||
use typst_library::text::{Font, Glyph, Lang, Region, TextElem, TextItem};
|
use typst_library::text::{Font, Glyph, Lang, Region, TextElem, TextItem};
|
||||||
@ -24,7 +24,8 @@ pub enum MathFragment {
|
|||||||
Glyph(GlyphFragment),
|
Glyph(GlyphFragment),
|
||||||
Variant(VariantFragment),
|
Variant(VariantFragment),
|
||||||
Frame(FrameFragment),
|
Frame(FrameFragment),
|
||||||
Spacing(Abs, bool),
|
Absolute(Abs, bool),
|
||||||
|
Fractional(Fr),
|
||||||
Space(Abs),
|
Space(Abs),
|
||||||
Linebreak,
|
Linebreak,
|
||||||
Align,
|
Align,
|
||||||
@ -41,7 +42,7 @@ impl MathFragment {
|
|||||||
Self::Glyph(glyph) => glyph.width,
|
Self::Glyph(glyph) => glyph.width,
|
||||||
Self::Variant(variant) => variant.frame.width(),
|
Self::Variant(variant) => variant.frame.width(),
|
||||||
Self::Frame(fragment) => fragment.frame.width(),
|
Self::Frame(fragment) => fragment.frame.width(),
|
||||||
Self::Spacing(amount, _) => *amount,
|
Self::Absolute(amount, _) => *amount,
|
||||||
Self::Space(amount) => *amount,
|
Self::Space(amount) => *amount,
|
||||||
_ => Abs::zero(),
|
_ => Abs::zero(),
|
||||||
}
|
}
|
||||||
@ -87,7 +88,8 @@ impl MathFragment {
|
|||||||
Self::Glyph(glyph) => glyph.class,
|
Self::Glyph(glyph) => glyph.class,
|
||||||
Self::Variant(variant) => variant.class,
|
Self::Variant(variant) => variant.class,
|
||||||
Self::Frame(fragment) => fragment.class,
|
Self::Frame(fragment) => fragment.class,
|
||||||
Self::Spacing(_, _) => MathClass::Space,
|
Self::Absolute(_, _) => MathClass::Space,
|
||||||
|
Self::Fractional(_) => MathClass::Space,
|
||||||
Self::Space(_) => MathClass::Space,
|
Self::Space(_) => MathClass::Space,
|
||||||
Self::Linebreak => MathClass::Space,
|
Self::Linebreak => MathClass::Space,
|
||||||
Self::Align => MathClass::Special,
|
Self::Align => MathClass::Special,
|
||||||
|
@ -75,7 +75,7 @@ pub fn layout_lr(
|
|||||||
fragments.retain(|fragment| {
|
fragments.retain(|fragment| {
|
||||||
let discard = (index == start_idx + 1 && opening_exists
|
let discard = (index == start_idx + 1 && opening_exists
|
||||||
|| index + 2 == end_idx && closing_exists)
|
|| index + 2 == end_idx && closing_exists)
|
||||||
&& matches!(fragment, MathFragment::Spacing(_, true));
|
&& matches!(fragment, MathFragment::Absolute(_, true));
|
||||||
index += 1;
|
index += 1;
|
||||||
!discard
|
!discard
|
||||||
});
|
});
|
||||||
|
@ -635,11 +635,17 @@ fn layout_h(
|
|||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
if let Spacing::Rel(rel) = elem.amount {
|
if elem.amount.is_zero() {
|
||||||
if rel.rel.is_zero() {
|
return Ok(());
|
||||||
ctx.push(MathFragment::Spacing(rel.abs.resolve(styles), elem.weak(styles)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.push(match elem.amount {
|
||||||
|
Spacing::Fr(fr) => MathFragment::Fractional(fr),
|
||||||
|
Spacing::Rel(rel) => MathFragment::Absolute(
|
||||||
|
rel.resolve(styles).relative_to(ctx.region.size.x),
|
||||||
|
elem.weak(styles),
|
||||||
|
),
|
||||||
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,14 +33,14 @@ impl MathRun {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicit spacing disables automatic spacing.
|
// Explicit spacing disables automatic spacing.
|
||||||
MathFragment::Spacing(width, weak) => {
|
MathFragment::Absolute(width, weak) => {
|
||||||
last = None;
|
last = None;
|
||||||
space = None;
|
space = None;
|
||||||
|
|
||||||
if weak {
|
if weak {
|
||||||
match resolved.last_mut() {
|
match resolved.last_mut() {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(MathFragment::Spacing(prev, true)) => {
|
Some(MathFragment::Absolute(prev, true)) => {
|
||||||
*prev = (*prev).max(width);
|
*prev = (*prev).max(width);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -52,6 +52,14 @@ impl MathRun {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same as explicit spacing that isn't weak.
|
||||||
|
MathFragment::Fractional(_) => {
|
||||||
|
last = None;
|
||||||
|
space = None;
|
||||||
|
resolved.push(fragment);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Alignment points are resolved later.
|
// Alignment points are resolved later.
|
||||||
MathFragment::Align => {
|
MathFragment::Align => {
|
||||||
resolved.push(fragment);
|
resolved.push(fragment);
|
||||||
@ -99,7 +107,7 @@ impl MathRun {
|
|||||||
resolved.push(fragment);
|
resolved.push(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(MathFragment::Spacing(_, true)) = resolved.last() {
|
if let Some(MathFragment::Absolute(_, true)) = resolved.last() {
|
||||||
resolved.pop();
|
resolved.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,10 +304,8 @@ impl MathRun {
|
|||||||
frame.translate(Point::with_y(ascent));
|
frame.translate(Point::with_y(ascent));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut space_is_visible = false;
|
|
||||||
|
|
||||||
let is_space = |f: &MathFragment| {
|
let is_space = |f: &MathFragment| {
|
||||||
matches!(f, MathFragment::Space(_) | MathFragment::Spacing(_, _))
|
matches!(f, MathFragment::Space(_) | MathFragment::Absolute(_, _))
|
||||||
};
|
};
|
||||||
let is_line_break_opportunity = |class, next_fragment| match class {
|
let is_line_break_opportunity = |class, next_fragment| match class {
|
||||||
// Don't split when two relations are in a row or when preceding a
|
// Don't split when two relations are in a row or when preceding a
|
||||||
@ -313,8 +319,21 @@ impl MathRun {
|
|||||||
|
|
||||||
let mut iter = self.0.into_iter().peekable();
|
let mut iter = self.0.into_iter().peekable();
|
||||||
while let Some(fragment) = iter.next() {
|
while let Some(fragment) = iter.next() {
|
||||||
if space_is_visible && is_space(&fragment) {
|
if let MathFragment::Fractional(fr) = fragment {
|
||||||
items.push(InlineItem::Space(fragment.width(), true));
|
if !empty {
|
||||||
|
let mut frame_prev =
|
||||||
|
std::mem::replace(&mut frame, Frame::soft(Size::zero()));
|
||||||
|
|
||||||
|
finalize_frame(&mut frame_prev, x, ascent, descent);
|
||||||
|
items.push(InlineItem::Frame(frame_prev));
|
||||||
|
empty = true;
|
||||||
|
|
||||||
|
x = Abs::zero();
|
||||||
|
ascent = Abs::zero();
|
||||||
|
descent = Abs::zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(InlineItem::Fractional(fr));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,14 +362,13 @@ impl MathRun {
|
|||||||
ascent = Abs::zero();
|
ascent = Abs::zero();
|
||||||
descent = Abs::zero();
|
descent = Abs::zero();
|
||||||
|
|
||||||
space_is_visible = true;
|
if iter.peek().map(is_space).is_some() {
|
||||||
if let Some(f_next) = iter.peek() {
|
while let Some(f_next) = iter.next_if(is_space) {
|
||||||
if !is_space(f_next) {
|
items.push(InlineItem::Absolute(f_next.width(), true));
|
||||||
items.push(InlineItem::Space(Abs::zero(), true));
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
items.push(InlineItem::Absolute(Abs::zero(), true));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
space_is_visible = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +453,7 @@ fn spacing(
|
|||||||
|
|
||||||
let resolve = |v: Em, size_ref: &MathFragment| -> Option<MathFragment> {
|
let resolve = |v: Em, size_ref: &MathFragment| -> Option<MathFragment> {
|
||||||
let width = size_ref.font_size().map_or(Abs::zero(), |size| v.at(size));
|
let width = size_ref.font_size().map_or(Abs::zero(), |size| v.at(size));
|
||||||
Some(MathFragment::Spacing(width, false))
|
Some(MathFragment::Absolute(width, false))
|
||||||
};
|
};
|
||||||
let script = |f: &MathFragment| f.math_size().is_some_and(|s| s <= MathSize::Script);
|
let script = |f: &MathFragment| f.math_size().is_some_and(|s| s <= MathSize::Script);
|
||||||
|
|
||||||
|
@ -174,7 +174,9 @@ impl Packed<InlineElem> {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum InlineItem {
|
pub enum InlineItem {
|
||||||
/// Absolute spacing between other items, and whether it is weak.
|
/// Absolute spacing between other items, and whether it is weak.
|
||||||
Space(Abs, bool),
|
Absolute(Abs, bool),
|
||||||
|
/// Fractional spacing between other items.
|
||||||
|
Fractional(Fr),
|
||||||
/// Layouted inline-level content.
|
/// Layouted inline-level content.
|
||||||
Frame(Frame),
|
Frame(Frame),
|
||||||
}
|
}
|
||||||
|
BIN
tests/ref/math-spacing-fractional-inline.png
Normal file
BIN
tests/ref/math-spacing-fractional-inline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
tests/ref/math-spacing-mixed-inline.png
Normal file
BIN
tests/ref/math-spacing-mixed-inline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/ref/math-spacing-relative-inline.png
Normal file
BIN
tests/ref/math-spacing-relative-inline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
BIN
tests/ref/math-spacing-relative.png
Normal file
BIN
tests/ref/math-spacing-relative.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 577 B |
@ -63,6 +63,46 @@ $#place(dx: 5em)[a] + b$
|
|||||||
// Validate that ignorant elements are layouted
|
// Validate that ignorant elements are layouted
|
||||||
#context test(counter("test").get(), (3,))
|
#context test(counter("test").get(), (3,))
|
||||||
|
|
||||||
|
--- math-spacing-relative ---
|
||||||
|
// Test relative spacing.
|
||||||
|
$ A #h(50%) B \
|
||||||
|
A#block(width: 50%);B \
|
||||||
|
A #block(width: 50%) B \
|
||||||
|
A space #h(50%) space B $
|
||||||
|
|
||||||
|
--- math-spacing-relative-inline ---
|
||||||
|
// Test relative spacing in inline math.
|
||||||
|
#let mtext = text.with(font: "Libertinus Serif")
|
||||||
|
Hello#h(40%)world \
|
||||||
|
Hello#box(width: 40%);world \
|
||||||
|
Hello$#h(40%)$world \
|
||||||
|
Hello$#box(width: 40%)$world \
|
||||||
|
$mtext("Hello") #h(40%) mtext("world")$ \
|
||||||
|
$mtext("Hello")#box(width: 40%);mtext("world")$
|
||||||
|
|
||||||
|
Hello #h(40%) world \
|
||||||
|
Hello #box(width: 40%) world \
|
||||||
|
Hello $#h(40%)$ world \
|
||||||
|
Hello $#box(width: 40%)$ world \
|
||||||
|
$mtext("Hello") #h(40%) space mtext("world")$ \
|
||||||
|
$mtext("Hello") #box(width: 40%) mtext("world")$
|
||||||
|
|
||||||
|
--- math-spacing-fractional-inline ---
|
||||||
|
// Test fractional spacing in inline math.
|
||||||
|
Hello #h(1fr) world \
|
||||||
|
Hello $#h(1fr)$ world
|
||||||
|
|
||||||
|
x #h(1fr) y \
|
||||||
|
$x #h(1fr) y$
|
||||||
|
|
||||||
|
Blah #h(1.5fr) long$#h(0.5fr) x - #h(1fr) y$ line. \
|
||||||
|
Blah #h(1.5fr) long $#h(0.5fr) x - #h(1fr) y$ line.
|
||||||
|
|
||||||
|
--- math-spacing-mixed-inline ---
|
||||||
|
// Test mixture of different kinds of spacing in inline math.
|
||||||
|
Some #h(30%) inline $x + #h(5%) y - #h(1fr) sum_(1 #h(1fr) 2) $ spacing #h(2fr) blah.
|
||||||
|
Long $(a #h(1fr) z) #h(1em, weak: true)$ #h(1%) $#h(0.5fr) sqrt(1 + #h(0.5fr) y)$.
|
||||||
|
|
||||||
--- issue-1052-math-number-spacing ---
|
--- issue-1052-math-number-spacing ---
|
||||||
// Test spacing after numbers in math.
|
// Test spacing after numbers in math.
|
||||||
$
|
$
|
||||||
|
Loading…
x
Reference in New Issue
Block a user