feat(epub): Japanese CJK line-breaking PR F — Utf8ClusterAssembler + viewport-width CJK wrapping#6
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final PR (PR F) of the Japanese CJK line-breaking effort. This is where behaviour changes: Japanese (CJK) paragraphs now wrap naturally at the viewport edge instead of breaking every ~66 characters. English / Latin / BiDi / hyphenation layout is unchanged.
Builds on PR D (ITextMetrics scaffolding + host characterization tests) and PR E (WordJoin enum + Utf8 predicates + parser bridge), both already on
master.Root cause
The parser flushed accumulated text into a "word" every
MAX_WORD_SIZE(200 bytes ≈ 66 Japanese chars). CJK has no spaces, so each 200-byte chunk became a layout word and the line broke after each one — leaving short ragged lines mid-paragraph. The real defect: the layout's join flag could only express "space + breakable" and "no-space + non-breakable", but never the "no-space + breakable" state CJK needs (you may break between any two ideographs, with no space). PR E generalised the flag to the three-stateWordJoin; this PR makes CJK characters first-class breakable units.Commits
feat(parser): add Utf8ClusterAssembler with grapheme cluster glue— new host-safe state machine (lib/Epub/Epub/parsers/Utf8ClusterAssembler.{h,cpp}) owning UTF-8 decode + grapheme-cluster assembly + WordJoin policy.staticmethods (external linkage so host gtest links directly), fixed buffers (no heap), no Arduino/HAL/expat. Handles cross-callback carry-over (pendingUtf8), base+extender clustering (pendingCjkBase), stage-time style snapshot, NUL-safe decode. + 12-case host suite (test/parser_cluster/).feat(parser): emit CJK as breakable words via Utf8ClusterAssembler— integrates the assembler intoChapterHtmlSlimParser:clusterStatemember,flushPendingText()/FlushedKind,currentFontStyle(), the 13 boundary flush sites + nested-table OPEN/DROP symmetric pair +startNewTextBlockdrainer, and the 750-token CJK ceiling. + 2 layout-proxy tests pinning the Latin→Glue / CJK→CjkBreak inline-close policy.chore: bump SECTION_FILE_VERSION + full verification— 26 → 27 so stale.crosspoint/sections/*.binregenerate (layout output changed; binary structure did not).docs/file-formats.mdsynced to v27.Design notes
currentFontStyle()is a verbatim extraction of the oldflushPartWordBufferstyle logic (including underline); the Latin emit path is unchanged. Only CJK produces the newCjkBreakstate.日<em>本</em>語stays wrappable (CJK keepsCjkBreak);quick<em>ly</em>stays glued (Latin keepsGlue). Driven byflushPendingText()returningFlushedKind.<u>flushes with underline even after</u>has closed.std::stringtoken per character; the 750-token ceiling (run after each CJK emit) bounds the transient layout vector, freed per page.Out of scope (deliberately deferred)
Kinsoku (禁則処理), vertical text, BiDi+CJK reorder fallback, cluster-aware rendering of extenders in GfxRenderer (extenders rasterise as normal-advance glyphs; layout keeps them on the same line), and parser-shell host-side test coverage (the parser TU pulls expat/HAL — not host-compilable). See the plan for rationale.
Verification
ParserClusterTest12/12,ParsedTextLayoutTest14/14 (incl. 2 new policy-proxy tests), full tree 132/132, 0 warnings under-Wall -Wextra -pedantic..crosspoint/first (also forced by the v27 bump).🤖 Generated with Claude Code