FA-80156 / Typography line breaking / Open access
Kerning and ligature line measure: kerning across spaces · case 01
Lines measure narrower when a word ends and the next begins with a kern pair.
ROOT CAUSE
Line width is measured on the words concatenated without spaces, so pairs kern across word boundaries.
THE FAILURE
Line width is measured on the words concatenated without spaces, so pairs kern across word boundaries.
Unsuccessful approach: Measuring the space-joined text treats the space as a default-width glyph.
Case contract
Input [words, width]. Glyph widths: i,l,j,.,, 4; t,f 6; m 15; w 14; M 16; W 18; other lowercase 10; other uppercase 13; space 5. Within a word, fi/fl/ff form ligatures (11/11/12) with no kerning into or out of them; case-sensitive kern pairs apply between adjacent glyphs; "|" is a non-joiner (invisible, blocks ligatures and kerning). No kerning across spaces. Greedy fill. Return [[text, width]].
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, width = x
W = {'i': 4, 'l': 4, 'j': 4, '.': 4, ',': 4, 't': 6, 'f': 6, 'm': 15, 'w': 14, 'M': 16, 'W': 18}
LIG = {'fi': 11, 'fl': 11, 'ff': 12}
KERN = {'AV': -3, 'VA': -3, 'To': -2, 'Ta': -2, 'Yo': -2, 'LT': -3, 'Wa': -2, 'av': -1, 'va': -1, 'y.': -2, 'r.': -2, 'l.': -1, 'f.': -1, 'Va': -2}
SPACE = 5
def glyph(c):
return W.get(c, 13 if c.isupper() else 10)
def measure(w):
total, prev, i = 0, None, 0
while i < len(w):
if w[i] == '|':
prev = None
i += 1
continue
pair = w[i:i + 2]
if pair in LIG:
total += LIG[pair]
prev = None
i += 2
continue
g = w[i]
total += glyph(g)
if prev is not None:
total += KERN.get(prev + g, 0)
prev = g
i += 1
return total
def line_width(ws):
return measure(''.join(ws)) + SPACE * (len(ws) - 1)
lines, cur = [], []
for w in words:
cand = cur + [w]
if not cur or line_width(cand) <= width:
cur = cand
else:
lines.append(cur)
cur = [w]
if cur:
lines.append(cur)
return [[' '.join(t.replace('|', '') for t in ln), line_width(ln)] for ln in lines]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('regression: kerning across spaces', [['Wavy', 'Ta', 'office', 'AT', 'office', 'shelf|life', 'flow'], 140], [['Wavy Ta office', 122], ['AT office', 77], ['shelflife flow', 104]]), ('partial-repair probe', [['LA|VALLE', 'av', 'Ta', 'LA|VALLE', 'dry.', 'wolf|like', 'wolf|like'], 51], [['LAVALLE', 88], ['av Ta', 45], ['LAVALLE', 88], ['dry.', 32], ['wolflike', 62], ['wolflike', 62]]), ('partial-repair probe', [['raffle', 'LA', 'fi', 'fl.', 'Wavy', 'raffle'], 128], [['raffle LA fi fl.', 113], ['Wavy raffle', 96]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('non-joiner blocks ligature', [['shelf|life'], 200], [['shelflife', 64]]), ('control layout', [['fly.', 'LA|VALLE'], 93], [['fly.', 23], ['LAVALLE', 88]]), ('control layout', [['our', 'Av'], 54], [['our', 30], ['Av', 23]])], [('regression: kerning across spaces', [['LA', 'VIA'], 102], [['LA VIA', 70]]), ('regression: kerning across spaces', [['AT', 'AT', 'office', 'Ta', 'staff.', 'Tofu'], 114], [['AT AT office', 108], ['Ta staff. Tofu', 110]]), ('partial-repair probe', [['shelf|life', 'LA|VALLE', 'raffle', 'y.', 'fl.'], 132], [['shelflife', 64], ['LAVALLE', 88], ['raffle y. fl.', 83]]), ('partial-repair probe', [['Tofu', 'LA'], 126], [['Tofu LA', 68]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('control layout', [['fi', 'dry.', 'wolf|like', 'VIA', 'Tofu'], 45], [['fi', 11], ['dry.', 32], ['wolflike', 62], ['VIA', 39], ['Tofu', 37]]), ('control layout', [['raffle', 'raffle'], 87], [['raffle', 46], ['raffle', 46]])], [('regression: kerning across spaces', [['fi', 'LT', 'office', 'Ta'], 104], [['fi LT office', 90], ['Ta', 21]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('regression: kerning across spaces', [['office', 'AT', 'our', 'fly.', 'flow', 'shelf|life'], 129], [['office AT our', 112], ['fly. flow', 63], ['shelflife', 64]]), ('partial-repair probe', [['Tofu', 'our', 'wolf|like', 'y.', 'flow', 'LT'], 91], [['Tofu our', 72], ['wolflike y.', 79], ['flow LT', 63]]), ('ligature then period', [['fl.', 'fi,'], 200], [['fl. fi,', 35]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('control layout', [['fly.', 'LA|VALLE'], 93], [['fly.', 23], ['LAVALLE', 88]]), ('control layout', [['Vat', 'VIA', 'Tofu', 'office', 'y.'], 62], [['Vat', 27], ['VIA', 39], ['Tofu', 37], ['office', 46], ['y.', 12]])], [('regression: kerning across spaces', [['shelf|life', 'AVAIL', 'Ta'], 103], [['shelflife', 64], ['AVAIL Ta', 85]]), ('regression: kerning across spaces', [['LA|VALLE', 'Wavy', 'fl.', 'AT', 'av', 'our'], 80], [['LAVALLE', 88], ['Wavy fl.', 65], ['AT av', 50], ['our', 30]]), ('partial-repair probe', [['VIA', 'AVAIL', 'VIA', 'raffle', 'raffle', 'wolf|like'], 91], [['VIA', 39], ['AVAIL', 59], ['VIA raffle', 90], ['raffle', 46], ['wolflike', 62]]), ('partial-repair probe', [['flow', 'VIA', 'Tofu', 'Av', 'fly.', 'Vat', 'Yo'], 74], [['flow', 35], ['VIA', 39], ['Tofu Av', 65], ['fly. Vat', 55], ['Yo', 21]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('control layout', [['fi', 'LA|VALLE'], 55], [['fi', 11], ['LAVALLE', 88]]), ('control layout', [['Av', 'Tofu'], 41], [['Av', 23], ['Tofu', 37]])], [('regression: kerning across spaces', [['flow', 'dry.', 'LA', 'LA', 'LT', 'av'], 77], [['flow dry.', 72], ['LA LA', 57], ['LT av', 47]]), ('regression: kerning across spaces', [['AT', 'LT', 'Tofu', 'LT', 'office', 'Tofu'], 111], [['AT LT Tofu', 96], ['LT office', 74], ['Tofu', 37]]), ('partial-repair probe', [['av', 'Av', 'Av'], 104], [['av Av Av', 75]]), ('partial-repair probe', [['LA|VALLE', 'shelf|life', 'office', 'AT', 'wolf|like'], 116], [['LAVALLE', 88], ['shelflife office', 115], ['AT wolflike', 93]]), ('ligature then period', [['fl.', 'fi,'], 200], [['fl. fi,', 35]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('control layout', [['AVAIL', 'LA|VALLE', 'staff.'], 78], [['AVAIL', 59], ['LAVALLE', 88], ['staff.', 42]]), ('control layout', [['VIA', 'LA|VALLE', 'Wavy', 'wolf|like'], 72], [['VIA', 39], ['LAVALLE', 88], ['Wavy', 45], ['wolflike', 62]])]]
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 |
|---|---|---|---|
| cross-word pair must not kern | [['LA Vat', 55]] | [['LA Vat', 58]] | Failed |
| regression: kerning across spaces | [['Wavy Ta office', 122], ['AT office', 75], ['shelflife flow', 104]] | [['Wavy Ta office', 122], ['AT office', 77], ['shelflife flow', 104]] | Failed |
| partial-repair probe | [['LAVALLE', 88], ['av Ta', 45], ['LAVALLE', 88], ['dry.', 32], ['wolflike', 62], ['wolflike', 62]] | [['LAVALLE', 88], ['av Ta', 45], ['LAVALLE', 88], ['dry.', 32], ['wolflike', 62], ['wolflike', 62]] | Passed |
| partial-repair probe | [['raffle LA fi fl.', 113], ['Wavy raffle', 96]] | [['raffle LA fi fl.', 113], ['Wavy raffle', 96]] | Passed |
| case-sensitive pair | [['Av av', 46]] | [['Av av', 47]] | Failed |
| non-joiner blocks ligature | [['shelflife', 64]] | [['shelflife', 64]] | Passed |
| control layout | [['fly.', 23], ['LAVALLE', 88]] | [['fly.', 23], ['LAVALLE', 88]] | Passed |
| control layout | [['our', 30], ['Av', 23]] | [['our', 30], ['Av', 23]] | Passed |
SHA-256 / 6f026cf2887bead2c152d506af6c5fe0b08bbd08134e646066698fb067140bd0
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
words, width = x
W = {'i': 4, 'l': 4, 'j': 4, '.': 4, ',': 4, 't': 6, 'f': 6, 'm': 15, 'w': 14, 'M': 16, 'W': 18}
LIG = {'fi': 11, 'fl': 11, 'ff': 12}
KERN = {'AV': -3, 'VA': -3, 'To': -2, 'Ta': -2, 'Yo': -2, 'LT': -3, 'Wa': -2, 'av': -1, 'va': -1, 'y.': -2, 'r.': -2, 'l.': -1, 'f.': -1, 'Va': -2}
SPACE = 5
def glyph(c):
return W.get(c, 13 if c.isupper() else 10)
def measure(w):
total, prev, i = 0, None, 0
while i < len(w):
if w[i] == '|':
prev = None
i += 1
continue
pair = w[i:i + 2]
if pair in LIG:
total += LIG[pair]
prev = None
i += 2
continue
g = w[i]
total += glyph(g)
if prev is not None:
total += KERN.get(prev + g, 0)
prev = g
i += 1
return total
def line_width(ws):
return measure(' '.join(ws))
lines, cur = [], []
for w in words:
cand = cur + [w]
if not cur or line_width(cand) <= width:
cur = cand
else:
lines.append(cur)
cur = [w]
if cur:
lines.append(cur)
return [[' '.join(t.replace('|', '') for t in ln), line_width(ln)] for ln in lines]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('regression: kerning across spaces', [['Wavy', 'Ta', 'office', 'AT', 'office', 'shelf|life', 'flow'], 140], [['Wavy Ta office', 122], ['AT office', 77], ['shelflife flow', 104]]), ('partial-repair probe', [['LA|VALLE', 'av', 'Ta', 'LA|VALLE', 'dry.', 'wolf|like', 'wolf|like'], 51], [['LAVALLE', 88], ['av Ta', 45], ['LAVALLE', 88], ['dry.', 32], ['wolflike', 62], ['wolflike', 62]]), ('partial-repair probe', [['raffle', 'LA', 'fi', 'fl.', 'Wavy', 'raffle'], 128], [['raffle LA fi fl.', 113], ['Wavy raffle', 96]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('non-joiner blocks ligature', [['shelf|life'], 200], [['shelflife', 64]]), ('control layout', [['fly.', 'LA|VALLE'], 93], [['fly.', 23], ['LAVALLE', 88]]), ('control layout', [['our', 'Av'], 54], [['our', 30], ['Av', 23]])], [('regression: kerning across spaces', [['LA', 'VIA'], 102], [['LA VIA', 70]]), ('regression: kerning across spaces', [['AT', 'AT', 'office', 'Ta', 'staff.', 'Tofu'], 114], [['AT AT office', 108], ['Ta staff. Tofu', 110]]), ('partial-repair probe', [['shelf|life', 'LA|VALLE', 'raffle', 'y.', 'fl.'], 132], [['shelflife', 64], ['LAVALLE', 88], ['raffle y. fl.', 83]]), ('partial-repair probe', [['Tofu', 'LA'], 126], [['Tofu LA', 68]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('control layout', [['fi', 'dry.', 'wolf|like', 'VIA', 'Tofu'], 45], [['fi', 11], ['dry.', 32], ['wolflike', 62], ['VIA', 39], ['Tofu', 37]]), ('control layout', [['raffle', 'raffle'], 87], [['raffle', 46], ['raffle', 46]])], [('regression: kerning across spaces', [['fi', 'LT', 'office', 'Ta'], 104], [['fi LT office', 90], ['Ta', 21]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('regression: kerning across spaces', [['office', 'AT', 'our', 'fly.', 'flow', 'shelf|life'], 129], [['office AT our', 112], ['fly. flow', 63], ['shelflife', 64]]), ('partial-repair probe', [['Tofu', 'our', 'wolf|like', 'y.', 'flow', 'LT'], 91], [['Tofu our', 72], ['wolflike y.', 79], ['flow LT', 63]]), ('ligature then period', [['fl.', 'fi,'], 200], [['fl. fi,', 35]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('control layout', [['fly.', 'LA|VALLE'], 93], [['fly.', 23], ['LAVALLE', 88]]), ('control layout', [['Vat', 'VIA', 'Tofu', 'office', 'y.'], 62], [['Vat', 27], ['VIA', 39], ['Tofu', 37], ['office', 46], ['y.', 12]])], [('regression: kerning across spaces', [['shelf|life', 'AVAIL', 'Ta'], 103], [['shelflife', 64], ['AVAIL Ta', 85]]), ('regression: kerning across spaces', [['LA|VALLE', 'Wavy', 'fl.', 'AT', 'av', 'our'], 80], [['LAVALLE', 88], ['Wavy fl.', 65], ['AT av', 50], ['our', 30]]), ('partial-repair probe', [['VIA', 'AVAIL', 'VIA', 'raffle', 'raffle', 'wolf|like'], 91], [['VIA', 39], ['AVAIL', 59], ['VIA raffle', 90], ['raffle', 46], ['wolflike', 62]]), ('partial-repair probe', [['flow', 'VIA', 'Tofu', 'Av', 'fly.', 'Vat', 'Yo'], 74], [['flow', 35], ['VIA', 39], ['Tofu Av', 65], ['fly. Vat', 55], ['Yo', 21]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('case-sensitive pair', [['Av', 'av'], 200], [['Av av', 47]]), ('control layout', [['fi', 'LA|VALLE'], 55], [['fi', 11], ['LAVALLE', 88]]), ('control layout', [['Av', 'Tofu'], 41], [['Av', 23], ['Tofu', 37]])], [('regression: kerning across spaces', [['flow', 'dry.', 'LA', 'LA', 'LT', 'av'], 77], [['flow dry.', 72], ['LA LA', 57], ['LT av', 47]]), ('regression: kerning across spaces', [['AT', 'LT', 'Tofu', 'LT', 'office', 'Tofu'], 111], [['AT LT Tofu', 96], ['LT office', 74], ['Tofu', 37]]), ('partial-repair probe', [['av', 'Av', 'Av'], 104], [['av Av Av', 75]]), ('partial-repair probe', [['LA|VALLE', 'shelf|life', 'office', 'AT', 'wolf|like'], 116], [['LAVALLE', 88], ['shelflife office', 115], ['AT wolflike', 93]]), ('ligature then period', [['fl.', 'fi,'], 200], [['fl. fi,', 35]]), ('cross-word pair must not kern', [['LA', 'Vat'], 200], [['LA Vat', 58]]), ('control layout', [['AVAIL', 'LA|VALLE', 'staff.'], 78], [['AVAIL', 59], ['LAVALLE', 88], ['staff.', 42]]), ('control layout', [['VIA', 'LA|VALLE', 'Wavy', 'wolf|like'], 72], [['VIA', 39], ['LAVALLE', 88], ['Wavy', 45], ['wolflike', 62]])]]
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 |
|---|---|---|---|
| cross-word pair must not kern | [['LA Vat', 63]] | [['LA Vat', 58]] | Failed |
| regression: kerning across spaces | [['Wavy Ta office', 132], ['AT office', 82], ['shelflife flow', 109]] | [['Wavy Ta office', 122], ['AT office', 77], ['shelflife flow', 104]] | Failed |
| partial-repair probe | [['LAVALLE', 88], ['av Ta', 50], ['LAVALLE', 88], ['dry.', 32], ['wolflike', 62], ['wolflike', 62]] | [['LAVALLE', 88], ['av Ta', 45], ['LAVALLE', 88], ['dry.', 32], ['wolflike', 62], ['wolflike', 62]] | Failed |
| partial-repair probe | [['raffle LA fi fl.', 128], ['Wavy raffle', 101]] | [['raffle LA fi fl.', 113], ['Wavy raffle', 96]] | Failed |
| case-sensitive pair | [['Av av', 52]] | [['Av av', 47]] | Failed |
| non-joiner blocks ligature | [['shelflife', 64]] | [['shelflife', 64]] | Passed |
| control layout | [['fly.', 23], ['LAVALLE', 88]] | [['fly.', 23], ['LAVALLE', 88]] | Passed |
| control layout | [['our', 30], ['Av', 23]] | [['our', 30], ['Av', 23]] | Passed |
SHA-256 / 550661cf4dfbd2aba1444f5a458c5d104b0e490daa0ae4dcb78db099b0ba8df3
HELD IN THE MEMBER ARCHIVE
The verified repair and its recorded checks are member-only.
This mechanism has 8 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.
Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.
Member access is invitation-based. Sign in with your invited account to inspect the repair.
Sign in to the archive ↗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:51.197522+00:00.
Case digest / 690d7c9a3613c3ab63ac9bda07ff372c4048409e4fff5c782d9217a8b33408fb