fix: align line walkers and Hangul jamo breaks · chenglou/pretext@c28eecb · GitHub
Skip to content

Commit c28eecb

Browse files
chengloumayrangvoidborne-dlttlin
committed
fix: align line walkers and Hangul jamo breaks
Normalize chunked batch line starts through the same segment-kind policy used by streaming so layoutWithLines(), walkLineRanges(), layoutNextLine(), and layout() stay aligned after zero-width break opportunities and collapsible spaces. Classify Hangul Compatibility Jamo (U+3130..U+318F) as CJK so common Korean compatibility jamo runs break like browser text. Refs #129, #135, #141. Closes #121 Closes #142 Co-authored-by: mayrang <pkss0626@naver.com> Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com> Co-authored-by: lttlin <lttlin@gmail.com>
1 parent 65f3a5c commit c28eecb

3 files changed

Lines changed: 60 additions & 17 deletions

File tree

src/analysis.ts

Lines changed: 1 addition & 0 deletions

src/layout.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function isWideCharacter(ch: string): boolean {
7979
(code >= 0x3000 && code <= 0x303F) ||
8080
(code >= 0x3040 && code <= 0x309F) ||
8181
(code >= 0x30A0 && code <= 0x30FF) ||
82+
(code >= 0x3130 && code <= 0x318F) ||
8283
(code >= 0xAC00 && code <= 0xD7AF) ||
8384
(code >= 0xFF00 && code <= 0xFFEF)
8485
)
@@ -552,6 +553,19 @@ describe('prepare invariants', () => {
552553
expect(prepareWithSegments('테스트입니다.', FONT).segments.at(-1)).toBe('다.')
553554
})
554555

556+
test('treats Hangul compatibility jamo as CJK break units', () => {
557+
const prepared = prepareWithSegments('ㅋㅋㅋ 진짜', FONT)
558+
expect(prepared.segments).toEqual(['ㅋ', 'ㅋ', 'ㅋ', ' ', '진', '짜'])
559+
560+
const width = measureWidth('ㅋㅋ', FONT) + 0.1
561+
const lines = layoutWithLines(prepared, width, LINE_HEIGHT)
562+
expect(lines.lines.map(line => line.text)).toEqual(['ㅋㅋ', 'ㅋ ', '진짜'])
563+
expect(layout(prepared, width, LINE_HEIGHT)).toEqual({
564+
lineCount: 3,
565+
height: LINE_HEIGHT * 3,
566+
})
567+
})
568+
555569
test('keeps non-CJK glue-connected runs intact before CJK text', () => {
556570
const prepared = prepareWithSegments('foo\u00A0世界', FONT)
557571
expect(prepared.segments).toEqual(['foo\u00A0', '世', '界'])
@@ -600,7 +614,8 @@ describe('prepare invariants', () => {
600614
}
601615
})
602616

603-
test('isCJK covers the newer CJK extension blocks', () => {
617+
test('isCJK covers Hangul compatibility jamo and the newer CJK extension blocks', () => {
618+
expect(isCJK('ㅋ')).toBe(true)
604619
expect(isCJK('\u{2EBF0}')).toBe(true)
605620
expect(isCJK('\u{31350}')).toBe(true)
606621
expect(isCJK('\u{323B0}')).toBe(true)
@@ -840,6 +855,16 @@ describe('layout invariants', () => {
840855
expect(layoutWithLines(prepared, width, LINE_HEIGHT).lines).toEqual(collectStreamedLines(prepared, width))
841856
})
842857

858+
test('chunked batch line walking normalizes spaces after zero-width breaks like streaming', () => {
859+
const prepared = prepareWithSegments('x\u00AD A\u200B B', FONT)
860+
const width = measureWidth('x A', FONT) + 0.1
861+
const batched = layoutWithLines(prepared, width, LINE_HEIGHT)
862+
863+
expect(batched.lines.map(line => line.text)).toEqual(['x A\u200B', 'B'])
864+
expect(collectStreamedLines(prepared, width)).toEqual(batched.lines)
865+
expect(layout(prepared, width, LINE_HEIGHT).lineCount).toBe(batched.lineCount)
866+
})
867+
843868
test('layoutNextLine can resume from any fixed-width line start without hidden state', () => {
844869
const prepared = prepareWithSegments('foo trans\u00ADatlantic said "hello" to 世界 and waved. alpha\u200Bbeta 🚀', FONT)
845870
const width = 90

src/line-break.ts

Lines changed: 33 additions & 16 deletions

0 commit comments

Comments
 (0)