mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Trim weak spacing at line start/end in paragraph layout (#4087)
This commit is contained in:
parent
171e1d6e83
commit
b73b3ca335
@ -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
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 |
BIN
tests/ref/trim-weak-space-line-beginning.png
Normal file
BIN
tests/ref/trim-weak-space-line-beginning.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 245 B |
BIN
tests/ref/trim-weak-space-line-end.png
Normal file
BIN
tests/ref/trim-weak-space-line-end.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 246 B |
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user