summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-09-17 13:56:23 +0200
committerGitHub <noreply@github.com>2024-09-17 11:56:23 +0000
commitc145e05f01746a72a9455d7fad95f927f568e2fd (patch)
treecabcc1683d0c680837b1c60afb1644d819aa7f0e
parentea145ff33bb6c3babb9765b5d0059612b2ea54f0 (diff)
Fix bad bound in forcibly overflowing optimal paragraph layout (#4975)
-rw-r--r--crates/typst/src/layout/inline/line.rs6
-rw-r--r--crates/typst/src/layout/inline/linebreak.rs12
-rw-r--r--tests/ref/issue-4651-justify-bad-bound.pngbin0 -> 207 bytes
-rw-r--r--tests/suite/layout/inline/justify.typ5
4 files changed, 22 insertions, 1 deletions
diff --git a/crates/typst/src/layout/inline/line.rs b/crates/typst/src/layout/inline/line.rs
index d930f707..1ac56e52 100644
--- a/crates/typst/src/layout/inline/line.rs
+++ b/crates/typst/src/layout/inline/line.rs
@@ -698,6 +698,12 @@ impl<'a> DerefMut for Items<'a> {
}
}
+impl Debug for Items<'_> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(&self.0).finish()
+ }
+}
+
/// A reference to or a boxed item.
pub enum ItemEntry<'a> {
Ref(&'a Item<'a>),
diff --git a/crates/typst/src/layout/inline/linebreak.rs b/crates/typst/src/layout/inline/linebreak.rs
index 7df65609..2dc3edb5 100644
--- a/crates/typst/src/layout/inline/linebreak.rs
+++ b/crates/typst/src/layout/inline/linebreak.rs
@@ -481,9 +481,19 @@ fn linebreak_optimized_approximate(
let Entry { end, breakpoint, unbreakable, .. } = table[idx];
let attempt = line(engine, p, start..end, breakpoint, Some(&pred));
- let (_, line_cost) =
+ let (ratio, line_cost) =
ratio_and_cost(p, metrics, width, &pred, &attempt, breakpoint, unbreakable);
+ // If approximation produces a valid layout without too much shrinking,
+ // exact layout is guaranteed to find the same layout. If, however, the
+ // line is overfull, we do not have this guarantee. Then, our bound
+ // becomes useless and actively harmful (it could be lower than what
+ // optimal layout produces). Thus, we immediately bail with an infinite
+ // bound in this case.
+ if ratio < metrics.min_ratio(false) {
+ return Cost::INFINITY;
+ }
+
pred = attempt;
start = end;
exact += line_cost;
diff --git a/tests/ref/issue-4651-justify-bad-bound.png b/tests/ref/issue-4651-justify-bad-bound.png
new file mode 100644
index 00000000..5b73fc66
--- /dev/null
+++ b/tests/ref/issue-4651-justify-bad-bound.png
Binary files differ
diff --git a/tests/suite/layout/inline/justify.typ b/tests/suite/layout/inline/justify.typ
index b35ff1fd..83fbd057 100644
--- a/tests/suite/layout/inline/justify.typ
+++ b/tests/suite/layout/inline/justify.typ
@@ -167,3 +167,8 @@ int main() {
// an underfull first line.
#set par(hanging-indent: 2.5cm, justify: true)
#lorem(5)
+
+--- issue-4651-justify-bad-bound ---
+// Test that overflow does not lead to bad bounds in paragraph optimization.
+#set par(justify: true)
+#block(width: 0pt)[A B]