The CJK layout code kept names from when it only handled Japanese.
Chinese regional presets, Pinyin, Zhuyin and Korean now share it, so
rename the modules and helpers to say what they do:
- kinsoku.rs becomes line_breaks.rs: the layout-text rewrite with
OffsetMap and the inserted break markers. The kinsoku predicates move
to jlreq.rs (was japanese.rs); JapaneseClass becomes JlreqClass.
- is_japanese_text_char becomes is_cjk_text_char.
- text_japanese.rs splits into text_attrs.rs (span attribute enums),
text_ruby.rs (ruby pieces and overhang), text_warichu.rs and
text_allocation.rs (capped_equal_allocations, shared by both axes).
- wasm/text/japanese_layout.rs becomes layout_attrs.rs.
- RubySide becomes AnnotationSide; RawTextSpan.ruby_side becomes
ruby_flags, since the byte packs the side and the Zhuyin bit.
- horizontal_aki_sheds becomes the horizontal_pair_spacing dispatcher
with a jlreq_aki_sheds branch; the plan field sheds is pair_spacing.
- The policy_* helpers become pairwise_* and the Japanese ones jlreq_*.
- The oikomi policy parameter is adjustment and minimum_oikomi_extent is
min_squeezed_extent.
Exported functions, Raw* enums and byte layouts stay unchanged.
AI-assisted-by: claude-opus-5-5
The CJK text options were behind per-language "Enable layout" toggles in
the file menu and the settings, each with its own sidebar section. A text
now has one language, picked in a single Language panel, and the options
of that language show below it.
- Rename the `vertical-text-layout` flag and feature to `cjk-support`.
- Remove the per-file and per-profile layout toggles, the file options
that stored them and the `:set-text-layout` change.
- Add the Language panel: a grouped dropdown of Japanese, Korean and the
regional Chinese variants, a row to change the language and a button to
remove it, "Mixed" when the selected texts differ. Picking a language
writes it to every paragraph of the texts.
- Render the CJK attrs only under the language that uses them, so a text
without language is horizontal and plain. The stored content keeps
every attr.
- Remember the last picked language for the next new texts.
- Add File > Language: the language of the texts without one of their
own, applied at render time and laid out again when it changes.
- Add the profile text language in Settings > Options: new files start
with it as their file language.
- Extract the grouped dropdown of the board size presets into a shared
component used by the presets and the Language panel.
- Name the Japanese readings furigana, drop the redundant prefixes of
the furigana and pinyin options, move Distribute below the writing
mode row and show only the position sides of the writing mode.
AI-assisted-by: claude-opus-5-5
Finding the line-end squeeze laid out the whole paragraph again for every
line, so the cost grew with the square of the length and typing in a
long Japanese text lagged.
Lines break greedily, so a line depends only on the text from its start.
Lay out a window from the line start that holds the line and the next
one, doubling it when needed, keep it while no squeeze changes the sheds,
and lay out the full paragraph once at the end. A test checks the sheds
match a full relayout.
AI-assisted-by: claude-opus-5-5
Readings paint above the line box, outside the glyph metrics. The text
bounds joined the emphasis marks only, so an auto-width text with ruby
or Zhuyin and no emphasis exported with the top of its reading cut off.
Measure the ink of the readings with the code that paints them and join
it to the content and auto-width bounds.
AI-assisted-by: claude-opus-5-5
The narrow-box overflow plan, which lets an indivisible unit overflow a
box narrower than one character, ran for Chinese and Korean only. In a
Japanese box of one character width Skia broke anywhere: a line started
with "、" and a ruby base split over two lines.
Run the plan for Japanese too, and refuse emergency breaks before a
character that may not start a line or after one that may not end it.
AI-assisted-by: claude-opus-5-5
Zhuyin, Pinyin and emphasis marks over horizontal text were placed
against an em box bottom-aligned to Skia's tight glyph rect, which spans
the hhea ascent and descent. In CJK faces the descent reaches past the
em box, so Zhuyin columns sat about 0.17 em below their base, and over
annotations relied on a hand-tuned clearance.
Place them against the ideographic em box (OS/2 typo metrics) of the
font that shaped each base, centred on the line baseline, the same
metrics vertical text uses for upright glyphs.
AI-assisted-by: claude-opus-5-5
Skia keys a line's style runs by UTF-8 byte offset, while line indices
are UTF-16. Decoration segments searched those runs with UTF-16 offsets,
so with CJK text every underline or strike on a line took the paint of
the first run, and a decorated span wrapped onto a new line lost its bar.
Look up each segment's run by the span's UTF-8 style anchor, falling
back to the line's first run for a span continued from an earlier line.
AI-assisted-by: claude-opus-5-5
Each ruby reading attached its lowest ink to the base, which suits kana
but not Latin letters: the descender of "y" lifted "yǒu" above "liáo"
over 友聊, and under ruby moved with ascenders and tone marks.
Western letters in a reading now take the font's descent (over) or cap
height (under) instead of their ink, so Pinyin and romaji readings share
one baseline and descenders stay clear of the base. Upright glyphs keep
the ink edge.
AI-assisted-by: claude-opus-5-5
SkParagraph moves each line right by half the letter-spacing of its first
character. That centres span tracking, but the spacing added to single
characters (distribution and justification gaps, squeezing, compression
and ruby room) must only open the gap after them. A distributed label of
two characters started half a gap in and overflowed the box by as much:
"国家" in a 200px box drew 国 92px from the left edge.
Builders of horizontal text record how far each adjusted character's
letter-spacing departs from its span tracking, and `LayoutParagraph` takes
back half of that on each line, next to its baseline shifts: painting,
glyph and placeholder rects, line metrics, hit testing and text paths.
Span tracking keeps its half spacing before the line.
AI-assisted-by: claude-opus-5-5
Add distributed alignment to text styles, sidebar controls, CSS export,
and the Plugin API. Spread characters across horizontal and vertical
lines, including the final line, while preserving graphemes and annotations.
Keep fractional line heights consistent across painting, hit testing,
selection, and text paths to prevent baseline drift between lines.
Add tests for distribution, nonbreaking text, and fractional line geometry.
AI-assisted-by: gpt-6
Recover merged annotation styles on the base character's own line, using
separate UTF-16 line anchors and UTF-8 style keys. Include emphasis marks
in horizontal text bounds so standalone exports keep each mark whole.
Add regression coverage for adjacent readings, wrapped text, emoji line
anchors, raster output and export bounds across all text grow modes.
Verify 670 renderer tests, formatting and both WASM builds. Add four
editable showcase examples and check their layout and exports.
AI-assisted-by: GPT-6
Retain legal line starts at the original wrap width before allowing an
indivisible punctuation or annotation unit to overflow together. Share the
layout plan and alignment offset across painting, editing and exports.
Cover mixed styles, nonbreaking controls, graphemes, annotations, alignment,
strokes and resize. All 666 renderer tests and both WASM builds pass.
AI-assisted-by: GPT-6
Add typed one-character Zhuyin readings with upright right-side symbols
and separate neutral and non-neutral tones in both writing modes.
Persist the mode through the editor, WASM bridge and Plugin API. Preserve
annotation clearance and canvas, PNG, PDF and SVG geometry.
Validate syntax and selection boundaries, and cover schema, bridge,
rendering, UI and API behavior. Native IME and reader review remain open.
AI-assisted-by: gpt-6
Carry optional span positions through the schema, WASM header, editor,
Plugin API and CSS output. Expose Chinese controls with language defaults
and independent overrides for horizontal and vertical text.
Reserve automatic clearance per side and stack underlines, ruby and
emphasis. Preserve reading groups during decoration edits and retain
canonical position values in DOM editor style data.
Add protocol, geometry, editing and API tests, including the prior Japanese
mixed-font gutter behavior. Verify both WASM targets and live exports.
AI-assisted-by: GPT-6
Support typed character and group readings through the shared ruby model.
Expose Chinese labels, presentation controls and selection guards without
changing Japanese controls or generating pronunciation.
Preserve complete source ranges for glyphs sharing a combining cluster.
Add pinned horizontal and vertical tone-mark fixtures and editor tests.
AI-assisted-by: GPT-6
Cover Chinese regional profiles and Korean wrap modes across fitting
widths, style boundaries, punctuation, numeric units and decomposed Jamo.
Check stable offsets and finite geometry in narrower horizontal boxes,
and keep indivisible punctuation units together in short vertical boxes.
AI-assisted-by: GPT-6
Add named Chinese punctuation presets and a Korean keep-words mode for
horizontal and vertical layout. Preserve the saved spaces-only mode and
keep source offsets stable through safe emergency wrapping.
Apply bounded CJK spacing and justification, expose the new choices in the
sidebar, and document preset bounds and remaining release checks.
Verify renderer, frontend, common and exporter tests, both WASM builds,
and sample save/reload plus PNG, PDF and SVG exports. Existing CI failures
remain documented in the layout notes.
AI-assisted-by: GPT-6
Choose paragraph layout rules from an optional language tag. Pass language
to horizontal and vertical shaping, add basic Chinese punctuation rules,
and add experimental Korean spaces-only wrapping.
Keep text boundaries grapheme-safe and preserve paragraph profiles across
editor splits and merges. Add reproducible font fixtures for localized
glyphs and regression tests for the shared WASM protocol and layout paths.
Validation: renderer, common JVM/JS, frontend and exporter tests pass.
Both WASM targets build. Full CI still reports baseline lint and format
errors and one backend multipart-upload test failure.
AI-assisted-by: GPT-6
Lay out horizontal and vertical Japanese text with kinsoku, ruby,
emphasis, warichu, tate-chu-yoko and proportional font features.
Carry the layout through glyph paths, SVG export and text selection.
Update the Rust and ClojureScript binary formats together, including
the editor bridge and headless exporter. Resolve CJK fallback fonts
and add renderer tests and font fixtures.