NC Litho · Docs

CJK typesetting plan

Last reviewed: 2026-10-10 · Last updated: 2026-10-10 · Change: trivial (status line and revision history added; content unchanged in the 2026-10-10 review) · Target: Adobe InDesign 2026

Goal: the Chinese, Japanese and Korean typesetting that InDesign offers with its East Asian feature set (the "Japanese/CJK" feature set, Feature Set Locale Setting 257), so that a Litho document can be set to the standard of a Japanese, Chinese or Korean publisher, round-trip through IDML, and export correct PDF and EPUB.

It builds on the CJK import and composition described in cjk-typography.md (composite fonts, kinsoku sets, aki, tsume, jidori, hanging punctuation, em alignment and leading models, kenten characters, tate-chu-yoko offsets, preserved mojikumi tables) and plans what remains.

Sources (clean-room)

Behaviour comes from public specifications, not from InDesign's implementation:

InDesign itself is only observed black-box (feature names, which options exist); no wording, presets or tables are copied. Our default mojikumi and kinsoku tables are built from JLREQ.

Where we are (2026-10)

Area State
Vertical text, upright CJK, rotated Latin done: story direction per story (frames of a thread agree; IDML StoryOrientation); glyphs turned by transforms; arrow keys follow the vertical lines
Vertical metrics and shaping done: upright runs shaped top to bottom (vert; vkrn/vpal when asked) with vmtx/VORG advances and origins, centred on the ideographic em box (BASE, else OS/2); turned runs shaped horizontally, without vrt2; Horizontal Kana (hkna) takes the vertical kana (vkna) in upright runs. Default location of variable fonts only (no VVAR)
Kinsoku named (hard, soft, Chinese, Korean) and custom kinsoku sets, bunri-kinshi, rensuuji, hanging punctuation (regular, force); without a set, fixed no-start / no-end lists and a break anywhere between CJK characters; kinsoku type (push in / push out priorities) kept but not applied
Korean line breaking at spaces (Hangul everywhere, hanja in Korean text); per-paragraph character-based breaking (koreanCharBreaks); Litho-only, not in IDML
Mojikumi tables and paragraph references kept through IDML; not applied (Preflight says so)
Aki, tsume, jidori done (cjk-typography.md)
Character alignment, leading model em box top / centre / bottom, ICF top / bottom (BASE icft/icfb, else the em box inset by 5%), roman baseline; aki above / below, centre (centre down as centre); under aki below a line's leading is the space to the line below it; with the Japanese composers (Composer::Japanese, JapaneseSingleLine, IDML HL Composer J / HL Single J), the others set lines baseline to baseline on the Roman baseline
Tate-chu-yoko manual, with offsets; auto tate-chu-yoko of short digit runs (with roman letters when asked) in vertical text, narrowed to fit the em (type.autoTateChuYoko, IDML AutoTcy, AutoTcyIncludeRoman); manual groups of 3+ digits overflow the em
Ruby group and per-character (jukugo) ruby; alignment (shoulder, centre, end, justify, JIS 1-2-1, equal aki, 1 ruby-character aki); above/right or below/left with offsets; font, size (½ by default), scales, OpenType ruby glyphs, auto tate-chu-yoko of digits; overhang onto kana and punctuation; parent spacing and automatic narrowing when the ruby is longer; flush with the line edge (a group stays on the line of each neighbour its ruby overhangs, so the reserved spacing holds at line edges); set beyond kenten on the same side; fill, stroke, tint, weight, and overprint in Overprint Preview; every setting through type.ruby and IDML. Doesn't affect leading; PDF export writes no overprint (Preflight and the export say so)
Kenten the kinds and a custom mark (up to 8 characters) in its own font; size, x/y scale, distance, above/right or below/left, centred or at the character's start; fill, stroke, tints, weight, and overprint in Overprint Preview (type.kenten, IDML Kenten*). The character set (input mode) is kept, not converted; PDF export writes no overprint
Shatai magnification, angle, adjust rotation and adjust tsume on screen, in PDF, outlines and type on a path (type.shatai, IDML Shatai*)
Rotate roman, roman word break half-width characters upright in vertical text (type.rotateRoman, IDML RotateSingleByteCharacters); breaks between Latin, Greek or Cyrillic letters without a hyphen (type.romanWordBreak, IDML AllowArbitraryHyphenation)
Grid alignment against the baseline grid: roman baseline, em box top / centre / bottom, ICF top / bottom; first line only; gyoudori (auto or N grid lines) and paragraph gyoudori (type.gridSettings, IDML GridAlignment, GridGyoudori, ParagraphGyoudori). No frame grid; vertical frames have no baseline grid
Composite fonts done: model, style.compositeFont.*, shaping, IDML, font list, replace and Preflight; no dialog
Languages Japanese, Korean, Simplified and Traditional Chinese reach the shaper (locl), font fallback, line breaking and typographer's quotes; IDML language names are kept as written; locale codes (ja_JP, ko-KR, de_DE_2006) are their language and region, and Chinese is also recognised in its common spellings ("Simplified Chinese", "Chinese (Traditional)", zh_CN, zh-Hant)
Missing glyphs as in InDesign: characters the applied font lacks are drawn as its missing-glyph box (screen and PDF) and listed by Preflight; the document setting Draw Missing Glyphs from Fallback Fonts (Preferences › Composition, off in new documents and IDML imports, on in documents saved before it existed) draws them from fallback fonts instead
Font menus Western families first, then Japanese, Simplified Chinese, Traditional Chinese and Korean groups (from the font's meta languages, CJK family names, OS/2 code pages, else its coverage); CJK families under their native names unless Preferences › Type › Show Font Names in English; the order is recalled from InDesign, not observed
CJK fonts Japanese faces (Shippori Mincho first) from the optional craft-fonts build input, in the font menus for users to apply; fallback by language (with fallback fonts on) (Japanese: the craft-fonts faces, then system fonts; Simplified and Traditional Chinese, Korean: system fonts); document fonts: the fonts of a Document Fonts folder beside an opened file belong to that document (composition, export, font menus), ahead of installed fonts of the same name, and are forgotten when it closes; Package copies every font file that draws text, fallback fonts included (licence permitting). Font files stay in memory for the session (reopening a document reuses them) and aren't read on the web
Interface Preferences › Type › Show CJK Features (cjkFeatures; until set, on for a Japanese, Chinese or Korean interface) shows the CJK-only menu items (Type › Tate-Chu-Yoko, Ruby…, Kenten, Warichu); layout, IDML and commands work either way
Everything else below missing

Known bugs to fix first:

Architecture

Character classes

A cjk::class_of(char, &Context) -> Class in litho-compose with the JLREQ classes (opening brackets, closing brackets, hyphens, dividing punctuation, middle dots, full stops, commas, inseparable characters, iteration marks, prolonged sound mark, small kana, prefixed and postfixed abbreviations, full-width ideographic space, hiragana, katakana, math symbols and operators, ideographs, Western characters, numerals, ruby bases, warichu brackets, …). Language and the paragraph's punctuation style decide the ambiguous ones (e.g. Chinese full-width punctuation centred vs corner-placed).

Spacing: mojikumi

A mojikumi set is a table indexed by (class before, class after) giving the space between them as a min/optimum/max triple in em fractions, plus line-start and line-end rules. Sets are document resources (Styles::mojikumi_tables, read from and written to IDML with their override rows), referenced by name from the paragraph's mojikumi; what remains is applying them. We ship a few built-in sets derived from JLREQ (no line-start indent, one-em indent, half-width punctuation, full-width punctuation…), all original data, for the IDML built-in names.

Shaping keeps the glyphs' own advances; mojikumi is applied after shaping as aki glue between glyphs: a full-width bracket's empty half becomes negative aki (compression) and the composer can widen or narrow each gap within its range. This keeps palt-style proportional fonts working (tsume removes the side bearings before mojikumi runs).

Line breaking: kinsoku and the Japanese composer

Vertical text, properly

Annotations and inline structures

All set after line layout like today's ruby, but able to affect line height:

Jidori, tsume, aki before and after, em-box alignment and the other leading models are in place (cjk-typography.md).

Grids

Fonts

Editing and output

Feature set (UI)

Preferences › Type gets a Feature Set choice (Western, CJK). CJK shows the East Asian controls in the Character and Paragraph panels, styles dialogs and menus (Kinsoku, Mojikumi, Composite Fonts, Named Grids, Layout Grid), makes the Japanese composer the default for new documents and offers the frame grid tools. Western hides them; documents keep their settings either way (opening a CJK document in Western mode loses nothing). Every feature stays reachable as a command regardless of the feature set (agents, MCP).

IDML attributes to support

Already read and written: KentenKind, KentenCustomCharacter, KentenFont, KentenFontStyle, KentenFontSize, KentenXScale, KentenYScale, KentenPlacement, KentenPosition, KentenAlignment, KentenCharacterSet, KentenFillColor, KentenStrokeColor, KentenTint, KentenStrokeTint, KentenWeight, KentenOverprintFill, KentenOverprintStroke, ShataiMagnification, ShataiDegreeAngle, ShataiAdjustRotation, ShataiAdjustTsume, RubyFlag (written as 1), RubyString, RubyType, RubyAlignment, RubyPosition, RubyXOffset, RubyYOffset, RubyFont, RubyFontStyle, RubyFontSize, RubyXScale, RubyYScale, RubyOpenTypePro, RubyAutoTcyDigits, RubyAutoTcyIncludeRoman, RubyAutoTcyAutoScale, RubyOverhang, RubyParentOverhangAmount, RubyParentSpacing, RubyAutoAlign, RubyAutoScaling, RubyParentScalingPercent, RubyFill, RubyStroke, RubyTint, RubyStrokeTint, RubyWeight, RubyOverprintFill, RubyOverprintStroke; paragraph AutoTcy, AutoTcyIncludeRoman, RotateSingleByteCharacters, AllowArbitraryHyphenation, GridAlignment, GridAlignFirstLineOnly, GridGyoudori, ParagraphGyoudori; the Warichu* attributes; Tatechuyoko, TatechuyokoXOffset, TatechuyokoYOffset, Jidori, Tsume, LeadingAki, TrailingAki, CharacterAlignment, LeadingModel, GlyphForm, OTFProportionalMetrics, OTFHVKana, OTFRomanItalics; paragraph KinsokuSet, KinsokuType, KinsokuHangType, BunriKinshi, Rensuuji, MojikumiTable, TreatIdeographicSpaceAsSpace; resources KinsokuTable, MojikumiTable, CompositeFont, StoryPreference direction.

Character: Rensuji, CharacterRotation.

Paragraph: Composer (the Japanese composers), CjkGridTracking, ScaleAffectsLineHeight.

Resources: MojikumiTableSet, NamedGrid, layout grid and frame grid preferences, StoryPreference orientation.

Exact names and enumerations are checked against the IDML specification as each phase lands.

Phases

Each phase lands as reviewable commits with tests (compose tests for layout, engine tests for commands, IDML round trips, a rendered sample page) and cargo xtask ci.

# Phase Contents Estimate (agent hours)
1 Foundations language passed to the shaper (locl); Simplified/Traditional Chinese and Korean languages; language-aware CJK fallback; document fonts folder; Korean word breaking; vmtx/VORG/em box and vertical shaping; story-level direction; vertical caret keys 12–17
2 Japanese composer character classes; mojikumi applied (built-in sets and the document's tables) as aki glue; JLREQ kinsoku sets and kinsoku types (push in / push out); Japanese paragraph and single-line composers; CJK justification and last line; Chinese punctuation styles; IDML 20–30
3 Inline CJK full ruby, kenten options, warichu, auto TCY and scale-to-fit, rensuji, shatai, character rotation, ICF alignment from the font; IDML 20–30
4 Grids layout grid, frame grids and tools, named grids, grid alignment and gyoudori, grid tracking, character count; IDML 20–30
5 Fonts Composite Fonts dialog and predefined character classes; CJK OpenType features in the OpenType menu 5–8
6 Editing and output kana/width-sensitive Find/Change, width and kana conversion, CJK numbering, quotes, EPUB/HTML vertical text and ruby 10–15
7 Feature set UI Preferences › Feature Set, panels and dialogs for every attribute, Japanese defaults for new documents, translations 15–20

Total: roughly 100–150 agent hours.

Phase 1 in detail

  1. CharProps.language reaches litho_fonts::shape (harfrust buffer language) and the word cache key; tests: a font with locl for JAN vs ZHS draws different glyphs for U+76F4.
  2. Language list: "Chinese: Simplified", "Chinese: Traditional", "Korean", "Japanese" (IDML names), with quotes and spelling/hyphenation off for them.
  3. Fallback chains by language in FontDb::fallback_for (system fonts per platform; the craft-fonts Japanese faces first for Japanese); tests with stand-in fonts and a fake catalog.
  4. Document fonts: opening a document loads the fonts in its Document Fonts folder for that document; Package copies used fonts there (licence permitting) and reports the ones it couldn't copy; tests with a synthetic package folder.
  5. Korean: cjk_break_between doesn't break between Hangul syllables unless the paragraph asks for character breaking; test with a Korean sentence.
  6. FontFace::v_advance/v_origin/em_box from vmtx/vhea/VORG/BASE; upright glyphs in vertical lines use them; tests against synthetic fonts with and without vmtx (without, the em box), and Shippori Mincho's tables when built with craft-fonts.
  7. Upright runs shaped top-to-bottom (vert, vkrn, vpal apply); vrt2 never on turned runs; test that a full-width comma takes its vertical form and position.
  8. Story direction at story level (frames of a story agree; IDML maps both ways); arrow keys move along the vertical line.

Decisions

Each follows what an InDesign user expects.

  1. Feature set gating. East Asian controls are hidden in the Western feature set and shown in the CJK feature set, as in InDesign. Every feature stays reachable as a command either way.
  2. Korean line breaking is word-based by default (KLREQ), with a per-paragraph option to break between syllables.
  3. PDF vertical text keeps transformed horizontal glyphs (correct-looking, copyable via ActualText) rather than vertical CID fonts (Identity-V).
  4. No CJK fonts in the repo; documents carry theirs in Document Fonts, scoped to the document as in InDesign. Litho embeds Japanese UI and document faces only when built with the optional craft-fonts input (release builds are).
  5. Package copies every font that draws text, fallback fonts included (when the document draws missing glyphs from them), so a package reproduces what is on screen.
  6. Korean fallback is serif first (Myungjo), as Chinese and Japanese fall back to Song and Mincho: the serif faces CJK editions of layout software default to.
  7. Language names on IDML import accept the common spellings of Simplified and Traditional Chinese, since the exact names InDesign writes are unconfirmed without a sample file.
  8. Missing glyphs are drawn as the font's box and reported, as in InDesign; drawing them from fallback fonts is a per-document choice, on only for documents saved before it existed.

Revision history

Date Change Summary
2026-10-10 trivial Reviewed in the full re-measure; status line and this table added; summarized in typography-parity.md and localization-parity.md
2026-10-10 minor Updated (see git history)
2026-10-06 major Created: CJK typesetting plan and status (the Japanese/CJK feature set)