Skip to content

fix: sync streaming line-break stepper with batch walker#135

Open
voidborne-d wants to merge 1 commit intochenglou:mainfrom
voidborne-d:fix/streaming-line-break-sync
Open

fix: sync streaming line-break stepper with batch walker#135
voidborne-d wants to merge 1 commit intochenglou:mainfrom
voidborne-d:fix/streaming-line-break-sync

Conversation

@voidborne-d
Copy link
Copy Markdown

Fixes #121

Two mismatches between layoutNextLine/layoutNextLineRange (streaming) and layoutWithLines/walkLineRanges (batch) caused the streaming APIs to produce different line breaks for certain inputs involving mixed scripts, soft hyphens, and non-simple whitespace modes.

1. maybeFinishAtSoftHyphen premature fallback (pre-wrap + soft-hyphen)

When an overflow occurred at a breakAfter segment (preserved-space, tab, etc.) and a soft-hyphen pending break was active, the step function's maybeFinishAtSoftHyphen had a fallback path that used the soft-hyphen break even when the segment's breakableFitAdvances was null. The batch walker's equivalent (continueSoftHyphenBreakableSegment) returns false in this case, allowing the closer breakAfter opportunity to be chosen instead.

Fix: Remove the fallback from maybeFinishAtSoftHyphen so the general pending-break check handles it, matching the batch walker's check order.

2. normalizeLineStartInChunk over-strips spaces (chunked path)

normalizeLineStartInChunk skipped space segments at line starts, but the chunked batch walker (walkPreparedLinesRaw non-simple branch) does not strip leading spaces when starting a new line within a chunk. This caused the streaming stepper to skip a leading space that the batch walker kept, producing fewer lines.

Fix: Condition the space-skip on simpleLineWalkFastPath so only the simple path strips spaces (matching normalizeSimpleLineStartSegmentIndex and the simple batch walker).

Reproduction

Using the reproducer from #121:

const text = "بام  \u200DB     bا \u00ADb\u060C b\f \u061F\uD83D\uDE80\u061F\u0639 \u0631 \u672C \u061F\na a A\u200B \u8A9E \u8A9E\u200D\u062D"
const prepared = prepareWithSegments(text, font)
const batchLines = layoutWithLines(prepared, 56.57, 20) // 8 lines
const streamLines = collectStreamedLines(prepared, 56.57) // was 7 lines, now 8

Tests

Added 3 regression tests:

  • pre-wrap soft-hyphen does not preempt a closer breakAfter opportunity
  • layoutNextLine keeps leading space inside a non-simple chunk when the batch walker does
  • mixed-script Arabic+CJK text keeps streaming aligned with batch layout

All 87 tests pass (bun test src/layout.test.ts).

Two mismatches between layoutNextLine/layoutNextLineRange (streaming) and
layoutWithLines/walkLineRanges (batch) caused the streaming APIs to produce
different line breaks for certain inputs involving mixed scripts, soft
hyphens, and non-simple whitespace modes.

1. maybeFinishAtSoftHyphen premature fallback (pre-wrap + soft-hyphen)

   When an overflow occurred at a breakAfter segment (preserved-space, tab,
   etc.) and a soft-hyphen pending break was active, the step function's
   maybeFinishAtSoftHyphen had a fallback path that used the soft-hyphen
   break even when the segment's breakableFitAdvances was null.  The batch
   walker's equivalent (continueSoftHyphenBreakableSegment) returns false
   in this case, allowing the closer breakAfter opportunity to be chosen
   instead.  Remove the fallback from maybeFinishAtSoftHyphen so the
   general pending-break check handles it, matching the batch walker order.

2. normalizeLineStartInChunk over-strips spaces (chunked path)

   normalizeLineStartInChunk skipped space segments at line starts, but
   the chunked batch walker (walkPreparedLinesRaw non-simple branch) does
   not strip leading spaces when starting a new line within a chunk.  This
   caused the streaming stepper to skip a leading space that the batch
   walker kept, producing fewer lines.  Condition the space-skip on
   simpleLineWalkFastPath so only the simple path strips spaces (matching
   normalizeSimpleLineStartSegmentIndex and the simple batch walker).

Add regression tests covering both fixes with the reproducer from chenglou#121.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: layoutNextLine and walkLineRanges mismatch with layoutWithLines

1 participant