Trim weak spacing at line start/end in paragraph layout (#4087)

This commit is contained in:
LU Jialin 2024-05-27 08:36:41 -07:00 committed by GitHub
parent 171e1d6e83
commit b73b3ca335
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 56 additions and 15 deletions

View File

@ -195,8 +195,8 @@ enum Segment<'a> {
/// One or multiple collapsed text or text-equivalent children. Stores how
/// long the segment is (in bytes of the full text string).
Text(usize),
/// Horizontal spacing between other segments.
Spacing(Spacing),
/// Horizontal spacing between other segments. Bool when true indicate weak space
Spacing(Spacing, bool),
/// A mathematical equation.
Equation(Vec<MathParItem>),
/// A box with arbitrary content.
@ -210,7 +210,7 @@ impl Segment<'_> {
fn len(&self) -> usize {
match *self {
Self::Text(len) => len,
Self::Spacing(_) => SPACING_REPLACE.len_utf8(),
Self::Spacing(_, _) => SPACING_REPLACE.len_utf8(),
Self::Box(_, frac) => {
(if frac { SPACING_REPLACE } else { OBJ_REPLACE }).len_utf8()
}
@ -231,7 +231,7 @@ enum Item<'a> {
/// A shaped text run with consistent style and direction.
Text(ShapedText<'a>),
/// Absolute spacing between other items.
Absolute(Abs),
Absolute(Abs, bool),
/// Fractional spacing between other items.
Fractional(Fr, Option<(&'a Packed<BoxElem>, StyleChain<'a>)>),
/// Layouted inline-level content.
@ -264,7 +264,7 @@ impl<'a> Item<'a> {
fn len(&self) -> usize {
match self {
Self::Text(shaped) => shaped.text.len(),
Self::Absolute(_) | Self::Fractional(_, _) => SPACING_REPLACE.len_utf8(),
Self::Absolute(_, _) | Self::Fractional(_, _) => SPACING_REPLACE.len_utf8(),
Self::Frame(_) => OBJ_REPLACE.len_utf8(),
Self::Tag(_) => 0,
Self::Skip(c) => c.len_utf8(),
@ -275,7 +275,7 @@ impl<'a> Item<'a> {
fn width(&self) -> Abs {
match self {
Self::Text(shaped) => shaped.width,
Self::Absolute(v) => *v,
Self::Absolute(v, _) => *v,
Self::Frame(frame) => frame.width(),
Self::Fractional(_, _) | Self::Tag(_) => Abs::zero(),
Self::Skip(_) => Abs::zero(),
@ -453,13 +453,13 @@ fn collect<'a>(
== TextElem::dir_in(*styles).start().into()
{
full.push(SPACING_REPLACE);
segments.push((Segment::Spacing(first_line_indent.into()), *styles));
segments.push((Segment::Spacing(first_line_indent.into(), false), *styles));
}
let hang = ParElem::hanging_indent_in(*styles);
if !hang.is_zero() {
full.push(SPACING_REPLACE);
segments.push((Segment::Spacing((-hang).into()), *styles));
segments.push((Segment::Spacing((-hang).into(), false), *styles));
}
let outer_dir = TextElem::dir_in(*styles);
@ -504,7 +504,7 @@ fn collect<'a>(
}
full.push(SPACING_REPLACE);
Segment::Spacing(*elem.amount())
Segment::Spacing(*elem.amount(), elem.weak(styles))
} else if let Some(elem) = child.to_packed::<LinebreakElem>() {
let c = if elem.justify(styles) { '\u{2028}' } else { '\n' };
full.push(c);
@ -618,10 +618,10 @@ fn prepare<'a>(
Segment::Text(_) => {
shape_range(&mut items, engine, &bidi, cursor..end, &spans, styles);
}
Segment::Spacing(spacing) => match spacing {
Segment::Spacing(spacing, weak) => match spacing {
Spacing::Rel(v) => {
let resolved = v.resolve(styles).relative_to(region.x);
items.push(Item::Absolute(resolved));
items.push(Item::Absolute(resolved, weak));
}
Spacing::Fr(v) => {
items.push(Item::Fractional(v, None));
@ -631,7 +631,8 @@ fn prepare<'a>(
items.push(Item::Skip(LTR_ISOLATE));
for item in par_items {
match item {
MathParItem::Space(s) => items.push(Item::Absolute(s)),
// MathParItem space are assumed to be weak space
MathParItem::Space(s) => items.push(Item::Absolute(s, true)),
MathParItem::Frame(mut frame) => {
frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame));
@ -1079,9 +1080,24 @@ fn line<'a>(
}
// Slice out the relevant items.
let (expanded, mut inner) = p.slice(range.clone());
let (mut expanded, mut inner) = p.slice(range.clone());
let mut width = Abs::zero();
// Weak space (Absolute(_, weak=true)) would be removed if at the end of the line
while let Some((Item::Absolute(_, true), before)) = inner.split_last() {
// apply it recursively to ensure the last one is not weak space
inner = before;
range.end -= 1;
expanded.end -= 1;
}
// Weak space (Absolute(_, weak=true)) would be removed if at the beginning of the line
while let Some((Item::Absolute(_, true), after)) = inner.split_first() {
// apply it recursively to ensure the first one is not weak space
inner = after;
range.start += 1;
expanded.end += 1;
}
// Reshape the last item if it's split in half or hyphenated.
let mut last = None;
let mut dash = None;
@ -1402,7 +1418,7 @@ fn commit(
};
match item {
Item::Absolute(v) => {
Item::Absolute(v, _) => {
offset += *v;
}
Item::Fractional(v, elem) => {

BIN
tests/ref/issue-4087.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

View File

@ -36,3 +36,25 @@ Totally #h() ignored
[Hello ]
counter(heading).display()
}
--- trim-weak-space-line-beginning ---
// Weak space at the beginning should be removed.
#h(2cm, weak: true) Hello
--- trim-weak-space-line-end ---
// Weak space at the end of the line should be removed.
#set align(right)
Hello #h(2cm, weak: true)
--- issue-4087 ---
// weak space at the end of the line would be removed.
This is the first line #h(2cm, weak: true) A new line
// non-weak space would be consume a specified width and push next line.
This is the first line #h(2cm, weak: false) A new line
// similarly weak space at the beginning of the line would be removed.
This is the first line\ #h(2cm, weak: true) A new line
// non-spacing, on the other hand, is not removed.
This is the first line\ #h(2cm, weak: false) A new line

View File

@ -86,9 +86,12 @@ Multiple trailing line breaks.
--- math-linebreaking-between-consecutive-relations ---
// A relation followed by a relation doesn't linebreak
// so essentially `a < = b` can be broken to `a` and `< = b`, `a < =` and `b`
// but never `a <` and `= b` because `< =` are consecutive relation that should
// be grouped together and no break between them.
#let hrule(x) = box(line(length: x))
#hrule(70pt)$a < = b$\
#hrule(74pt)$a < = b$
#hrule(78pt)$a < = b$
--- math-linebreaking-after-relation-without-space ---
// Line breaks can happen after a relation even if there is no