FA-80086 / Typography line breaking / Open access
Justified interword spacing: ragged fallback spacing · case 01
Ragged lines are set with the wrong space width.
ROOT CAUSE
The ragged setter uses the minimum space instead of the natural space.
VERIFIED REPAIR
Set ragged lines with the natural space.
Unsuccessful approach: Using the maximum space is just as wrong in the other direction.
Case contract
Input [word widths, target width, is_last, natural space, min space, max space]. Last lines and single-word lines are set ragged with natural spaces. Otherwise distribute target-sum(words) over the gaps: equal share with the remainder units added to the rightmost gaps; a share below min space is ["overfull"]; if any gap would exceed max space fall back to ragged. Return word x positions.
Why this case matters
Line breaking decides where paragraphs wrap on screen and in print; a wrong decision point shifts every following line.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
words, target, is_last, sp, lo, hi = x
n = len(words)
gaps = n - 1
def natural():
pos, xx = [], 0
for w in words:
pos.append(xx)
xx += w + lo
return pos
if is_last or gaps == 0:
return natural()
space = target - sum(words)
per, rem = divmod(space, gaps)
if per < lo:
return ['overfull']
if per + (1 if rem else 0) > hi:
return natural()
pos, xx = [], 0
for k, w in enumerate(words):
pos.append(xx)
if k < gaps:
xx += w + per + (1 if k >= gaps - rem else 0)
return pos
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('regression: ragged fallback spacing', [[4, 7, 7, 7], 32, True, 2, 1, 3], [0, 6, 15, 24]), ('regression: ragged fallback spacing', [[3, 5], 14, False, 3, 2, 4], [0, 6]), ('partial-repair probe', [[3, 9], 17, False, 3, 3, 4], [0, 6]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[9, 9, 9, 5, 4, 4], 48, False, 3, 3, 4], ['overfull']), ('control layout', [[6], 7, False, 2, 2, 4], [0])], [('regression: ragged fallback spacing', [[7, 9, 6], 27, True, 3, 1, 4], [0, 10, 22]), ('regression: ragged fallback spacing', [[8, 5, 5, 8, 5], 57, True, 3, 2, 5], [0, 11, 19, 27, 38]), ('regression: ragged fallback spacing', [[6, 6], 22, False, 3, 1, 7], [0, 9]), ('regression: ragged fallback spacing', [[3, 3, 4, 4, 5], 38, False, 2, 1, 3], [0, 5, 10, 16, 22]), ('single word line', [[7], 20, False, 2, 1, 4], [0]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[7, 2, 5, 3], 21, False, 2, 1, 5], [0, 8, 11, 18]), ('control layout', [[3, 3], 8, False, 3, 1, 5], [0, 5])], [('regression: ragged fallback spacing', [[7, 6], 18, False, 2, 1, 3], [0, 9]), ('regression: ragged fallback spacing', [[6, 3, 4, 3], 30, False, 2, 1, 4], [0, 8, 13, 19]), ('partial-repair probe', [[7, 4, 2, 7, 4], 25, True, 3, 3, 6], [0, 10, 17, 22, 32]), ('regression: ragged fallback spacing', [[7, 4], 16, False, 2, 1, 3], [0, 9]), ('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('control layout', [[2, 8, 3, 8], 27, False, 3, 3, 6], ['overfull']), ('control layout', [[8], 9, False, 2, 2, 4], [0])], [('regression: ragged fallback spacing', [[6, 6], 22, False, 3, 1, 7], [0, 9]), ('regression: ragged fallback spacing', [[4, 7], 18, False, 3, 2, 6], [0, 7]), ('partial-repair probe', [[7, 8, 3, 7, 9], 37, True, 2, 2, 5], [0, 9, 19, 24, 33]), ('regression: ragged fallback spacing', [[8, 4], 18, False, 3, 2, 5], [0, 11]), ('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('control layout', [[3, 7, 7, 8, 9], 41, False, 2, 1, 3], [0, 4, 13, 22, 32]), ('control layout', [[6, 3, 2, 7, 8, 2], 47, False, 3, 3, 6], [0, 9, 16, 22, 33, 45])], [('regression: ragged fallback spacing', [[5, 5, 6, 4, 2], 50, False, 2, 1, 5], [0, 7, 14, 22, 28]), ('regression: ragged fallback spacing', [[9, 8, 3, 8], 40, False, 2, 1, 3], [0, 11, 21, 26]), ('regression: ragged fallback spacing', [[7, 2, 9, 2, 4, 6], 59, True, 2, 1, 4], [0, 9, 13, 24, 28, 34]), ('regression: ragged fallback spacing', [[6, 3, 8], 28, False, 3, 2, 5], [0, 9, 15]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[6, 4, 8, 8, 2], 31, False, 3, 1, 4], ['overfull']), ('control layout', [[5, 8, 2, 5, 5], 30, False, 3, 1, 5], [0, 6, 15, 18, 25])]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| last line ragged | [0, 4, 9] | [0, 5, 11] | Failed |
| regression: ragged fallback spacing | [0, 5, 13, 21] | [0, 6, 15, 24] | Failed |
| regression: ragged fallback spacing | [0, 5] | [0, 6] | Failed |
| partial-repair probe | [0, 6] | [0, 6] | Passed |
| exactly minimum share | [0, 4, 8] | [0, 4, 8] | Passed |
| remainder goes to right gaps | [0, 5, 11, 18] | [0, 5, 11, 18] | Passed |
| control layout | ['overfull'] | ['overfull'] | Passed |
| control layout | [0] | [0] | Passed |
SHA-256 / bc164e8cc4bc0624cea78f9c953e545a3fe3960a0893e16480a85276b10eb385
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
words, target, is_last, sp, lo, hi = x
n = len(words)
gaps = n - 1
def natural():
pos, xx = [], 0
for w in words:
pos.append(xx)
xx += w + hi
return pos
if is_last or gaps == 0:
return natural()
space = target - sum(words)
per, rem = divmod(space, gaps)
if per < lo:
return ['overfull']
if per + (1 if rem else 0) > hi:
return natural()
pos, xx = [], 0
for k, w in enumerate(words):
pos.append(xx)
if k < gaps:
xx += w + per + (1 if k >= gaps - rem else 0)
return pos
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('regression: ragged fallback spacing', [[4, 7, 7, 7], 32, True, 2, 1, 3], [0, 6, 15, 24]), ('regression: ragged fallback spacing', [[3, 5], 14, False, 3, 2, 4], [0, 6]), ('partial-repair probe', [[3, 9], 17, False, 3, 3, 4], [0, 6]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[9, 9, 9, 5, 4, 4], 48, False, 3, 3, 4], ['overfull']), ('control layout', [[6], 7, False, 2, 2, 4], [0])], [('regression: ragged fallback spacing', [[7, 9, 6], 27, True, 3, 1, 4], [0, 10, 22]), ('regression: ragged fallback spacing', [[8, 5, 5, 8, 5], 57, True, 3, 2, 5], [0, 11, 19, 27, 38]), ('regression: ragged fallback spacing', [[6, 6], 22, False, 3, 1, 7], [0, 9]), ('regression: ragged fallback spacing', [[3, 3, 4, 4, 5], 38, False, 2, 1, 3], [0, 5, 10, 16, 22]), ('single word line', [[7], 20, False, 2, 1, 4], [0]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[7, 2, 5, 3], 21, False, 2, 1, 5], [0, 8, 11, 18]), ('control layout', [[3, 3], 8, False, 3, 1, 5], [0, 5])], [('regression: ragged fallback spacing', [[7, 6], 18, False, 2, 1, 3], [0, 9]), ('regression: ragged fallback spacing', [[6, 3, 4, 3], 30, False, 2, 1, 4], [0, 8, 13, 19]), ('partial-repair probe', [[7, 4, 2, 7, 4], 25, True, 3, 3, 6], [0, 10, 17, 22, 32]), ('regression: ragged fallback spacing', [[7, 4], 16, False, 2, 1, 3], [0, 9]), ('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('control layout', [[2, 8, 3, 8], 27, False, 3, 3, 6], ['overfull']), ('control layout', [[8], 9, False, 2, 2, 4], [0])], [('regression: ragged fallback spacing', [[6, 6], 22, False, 3, 1, 7], [0, 9]), ('regression: ragged fallback spacing', [[4, 7], 18, False, 3, 2, 6], [0, 7]), ('partial-repair probe', [[7, 8, 3, 7, 9], 37, True, 2, 2, 5], [0, 9, 19, 24, 33]), ('regression: ragged fallback spacing', [[8, 4], 18, False, 3, 2, 5], [0, 11]), ('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('control layout', [[3, 7, 7, 8, 9], 41, False, 2, 1, 3], [0, 4, 13, 22, 32]), ('control layout', [[6, 3, 2, 7, 8, 2], 47, False, 3, 3, 6], [0, 9, 16, 22, 33, 45])], [('regression: ragged fallback spacing', [[5, 5, 6, 4, 2], 50, False, 2, 1, 5], [0, 7, 14, 22, 28]), ('regression: ragged fallback spacing', [[9, 8, 3, 8], 40, False, 2, 1, 3], [0, 11, 21, 26]), ('regression: ragged fallback spacing', [[7, 2, 9, 2, 4, 6], 59, True, 2, 1, 4], [0, 9, 13, 24, 28, 34]), ('regression: ragged fallback spacing', [[6, 3, 8], 28, False, 3, 2, 5], [0, 9, 15]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[6, 4, 8, 8, 2], 31, False, 3, 1, 4], ['overfull']), ('control layout', [[5, 8, 2, 5, 5], 30, False, 3, 1, 5], [0, 6, 15, 18, 25])]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| last line ragged | [0, 9, 19] | [0, 5, 11] | Failed |
| regression: ragged fallback spacing | [0, 7, 17, 27] | [0, 6, 15, 24] | Failed |
| regression: ragged fallback spacing | [0, 7] | [0, 6] | Failed |
| partial-repair probe | [0, 7] | [0, 6] | Failed |
| exactly minimum share | [0, 4, 8] | [0, 4, 8] | Passed |
| remainder goes to right gaps | [0, 5, 11, 18] | [0, 5, 11, 18] | Passed |
| control layout | ['overfull'] | ['overfull'] | Passed |
| control layout | [0] | [0] | Passed |
SHA-256 / 526f47141320855f57d770065be399a7776f138a1efa2f59376328e64c21aa88
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
words, target, is_last, sp, lo, hi = x
n = len(words)
gaps = n - 1
def natural():
pos, xx = [], 0
for w in words:
pos.append(xx)
xx += w + sp
return pos
if is_last or gaps == 0:
return natural()
space = target - sum(words)
per, rem = divmod(space, gaps)
if per < lo:
return ['overfull']
if per + (1 if rem else 0) > hi:
return natural()
pos, xx = [], 0
for k, w in enumerate(words):
pos.append(xx)
if k < gaps:
xx += w + per + (1 if k >= gaps - rem else 0)
return pos
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('regression: ragged fallback spacing', [[4, 7, 7, 7], 32, True, 2, 1, 3], [0, 6, 15, 24]), ('regression: ragged fallback spacing', [[3, 5], 14, False, 3, 2, 4], [0, 6]), ('partial-repair probe', [[3, 9], 17, False, 3, 3, 4], [0, 6]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[9, 9, 9, 5, 4, 4], 48, False, 3, 3, 4], ['overfull']), ('control layout', [[6], 7, False, 2, 2, 4], [0])], [('regression: ragged fallback spacing', [[7, 9, 6], 27, True, 3, 1, 4], [0, 10, 22]), ('regression: ragged fallback spacing', [[8, 5, 5, 8, 5], 57, True, 3, 2, 5], [0, 11, 19, 27, 38]), ('regression: ragged fallback spacing', [[6, 6], 22, False, 3, 1, 7], [0, 9]), ('regression: ragged fallback spacing', [[3, 3, 4, 4, 5], 38, False, 2, 1, 3], [0, 5, 10, 16, 22]), ('single word line', [[7], 20, False, 2, 1, 4], [0]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[7, 2, 5, 3], 21, False, 2, 1, 5], [0, 8, 11, 18]), ('control layout', [[3, 3], 8, False, 3, 1, 5], [0, 5])], [('regression: ragged fallback spacing', [[7, 6], 18, False, 2, 1, 3], [0, 9]), ('regression: ragged fallback spacing', [[6, 3, 4, 3], 30, False, 2, 1, 4], [0, 8, 13, 19]), ('partial-repair probe', [[7, 4, 2, 7, 4], 25, True, 3, 3, 6], [0, 10, 17, 22, 32]), ('regression: ragged fallback spacing', [[7, 4], 16, False, 2, 1, 3], [0, 9]), ('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('control layout', [[2, 8, 3, 8], 27, False, 3, 3, 6], ['overfull']), ('control layout', [[8], 9, False, 2, 2, 4], [0])], [('regression: ragged fallback spacing', [[6, 6], 22, False, 3, 1, 7], [0, 9]), ('regression: ragged fallback spacing', [[4, 7], 18, False, 3, 2, 6], [0, 7]), ('partial-repair probe', [[7, 8, 3, 7, 9], 37, True, 2, 2, 5], [0, 9, 19, 24, 33]), ('regression: ragged fallback spacing', [[8, 4], 18, False, 3, 2, 5], [0, 11]), ('last line ragged', [[3, 4, 5], 20, True, 2, 1, 6], [0, 5, 11]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('control layout', [[3, 7, 7, 8, 9], 41, False, 2, 1, 3], [0, 4, 13, 22, 32]), ('control layout', [[6, 3, 2, 7, 8, 2], 47, False, 3, 3, 6], [0, 9, 16, 22, 33, 45])], [('regression: ragged fallback spacing', [[5, 5, 6, 4, 2], 50, False, 2, 1, 5], [0, 7, 14, 22, 28]), ('regression: ragged fallback spacing', [[9, 8, 3, 8], 40, False, 2, 1, 3], [0, 11, 21, 26]), ('regression: ragged fallback spacing', [[7, 2, 9, 2, 4, 6], 59, True, 2, 1, 4], [0, 9, 13, 24, 28, 34]), ('regression: ragged fallback spacing', [[6, 3, 8], 28, False, 3, 2, 5], [0, 9, 15]), ('exactly minimum share', [[3, 3, 3], 11, False, 2, 1, 4], [0, 4, 8]), ('remainder goes to right gaps', [[3, 4, 5, 2], 20, False, 2, 1, 4], [0, 5, 11, 18]), ('control layout', [[6, 4, 8, 8, 2], 31, False, 3, 1, 4], ['overfull']), ('control layout', [[5, 8, 2, 5, 5], 30, False, 3, 1, 5], [0, 6, 15, 18, 25])]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| last line ragged | [0, 5, 11] | [0, 5, 11] | Passed |
| regression: ragged fallback spacing | [0, 6, 15, 24] | [0, 6, 15, 24] | Passed |
| regression: ragged fallback spacing | [0, 6] | [0, 6] | Passed |
| partial-repair probe | [0, 6] | [0, 6] | Passed |
| exactly minimum share | [0, 4, 8] | [0, 4, 8] | Passed |
| remainder goes to right gaps | [0, 5, 11, 18] | [0, 5, 11, 18] | Passed |
| control layout | ['overfull'] | ['overfull'] | Passed |
| control layout | [0] | [0] | Passed |
SHA-256 / 936a40fb60c3d12e42eed698758c5c419d44503a5565fcbb65e6fc22b53affc4
Verification & scope
A deterministic toy typesetting model with integer widths and a stipulated rule set; it does not claim conformance to any engine. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:50.556904+00:00.
Case digest / f443d18744f035036e9215c8e77f287cfb7614a6da29e17b0fd1faf62c863e8e