mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 19:27:07 -04:00
Merge origin/main into data-merge
Resolve conflicts keeping both sides: doc gains both `cjk` and `datamerge` modules; file.open resolves data-merge sources and loads the document's fonts; DocState keeps preview_stash/preview_record and fonts. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
48dfbcfd19
105 files changed
+11338
-710
No files matched your search
@@ -0,0 +1,76 @@
|
||||
# Arabic import and composition
|
||||
|
||||
Task: AR1. These changes repair verified gaps in the existing typesetting pipeline.
|
||||
They do not claim full InDesign or vendor-specific Naskh composition parity.
|
||||
|
||||
## Architecture
|
||||
|
||||
Portable character controls live in `doc::arabic` and the inherited attribute
|
||||
model. Story column direction and table column direction are independent of
|
||||
paragraph direction. The IDML adapter translates serialized values at the boundary.
|
||||
The font layer resolves script/direction runs and passes language and surrounding
|
||||
text to the OpenType shaper. The composer retains complete cluster text for
|
||||
paragraph-level bidi resolution, then applies line-level ordering and placement.
|
||||
The renderer continues to consume placed glyphs without Arabic-specific policy.
|
||||
|
||||
Existing `type.char`, `type.para` and `table.options` commands edit the controls.
|
||||
`story.setDirection` edits column flow with history and cache invalidation;
|
||||
`story.get` reports it. Table cell indices remain logical even when their visual
|
||||
positions are reversed. Physical left/right insertion commands account for direction.
|
||||
|
||||
## Implemented rules
|
||||
|
||||
| Area | Behavior |
|
||||
| --- | --- |
|
||||
| Mixed scripts | Separate Hebrew/Arabic script runs, resolved shaping direction, language tags and OpenType joining features |
|
||||
| Style boundaries | Pre/post shaping context preserves joining when a character style changes within an Arabic word |
|
||||
| Font fallback | Combining marks and join controls stay with the preceding base face |
|
||||
| Bidirectional layout | Full paragraph context, cluster contents and Unicode controls survive line breaking; per-line UAX #9 ordering keeps marks attached, including lines with tab leaders |
|
||||
| Character direction | Imported default/LTR/RTL overrides participate in bidi resolution; single-character punctuation mirroring is corrected against final paragraph levels |
|
||||
| Digits | European, Arabic-Indic and Persian conversion in both directions, contextual defaults, language-sensitive native digits, original UTF-8 source ranges retained |
|
||||
| Diacritics | Font GDEF classification and OpenType mark anchors; explicit X/Y offsets affect marks only, in thousandths of an em |
|
||||
| Positional forms | Explicit isolated/initial/medial/final OpenType feature selection; automatic forms remain the default |
|
||||
| Kashidas | Paragraph and character switches, actual font coverage, shaper-approved insertion boundaries, nonjoiner protection, whole-cluster movement to keep marks attached |
|
||||
| Story direction | Independent right-to-left column flow, including span-column bounds |
|
||||
| Table direction | Right-to-left cell placement, merged spans, physical edge styles/borders, hit testing and left/right column insertion |
|
||||
| Serialization | Character controls, paragraph policy/width values, story/table directions survive native data and IDML round trips |
|
||||
|
||||
## Remaining limitations
|
||||
|
||||
- Vendor-specific Arabic/Naskh justification policies and Paragraph Kashida Width
|
||||
presets are preserved but not reproduced. Preflight reports their use.
|
||||
Automatic elongation still uses bounded, scaled tatweel insertion rather than
|
||||
full line reshaping or a vendor allocation algorithm.
|
||||
- Loose/Medium/Tight/OpenType-from-baseline diacritic presets are preserved and
|
||||
reported. Composition uses font anchors and explicit offsets instead.
|
||||
- Script-specific stretched alternates, contextual swash policies, complete RTL
|
||||
tab-stop semantics and re-shaping at emergency intra-word line breaks require
|
||||
further compatibility work. Tracking and arbitrary metric changes within a
|
||||
cursive word can still open joining gaps.
|
||||
- A font must contain the required glyphs and appropriate OpenType tables. This
|
||||
change neither embeds proprietary fonts nor makes missing-font errors disappear.
|
||||
- CJK compatibility boundaries remain documented in `cjk-typography.md`.
|
||||
|
||||
## Verification and sources
|
||||
|
||||
Regression tests cover independent IDML XML, round trips, forced line breaks,
|
||||
character overrides, original byte ranges, mark placement, style-boundary joining,
|
||||
kashida controls, story/table flow, merged cells, command validation and undo.
|
||||
Font-dependent tests use available fonts; no proprietary font or user document
|
||||
is committed. Synthetic Arabic documents provide visual smoke checks.
|
||||
|
||||
The AR1 validation run passed all six `cargo xtask ci` gates and 616 tests
|
||||
(with two existing ignored tests). WASM checks used the installed Rust standard
|
||||
library sources via `-Z build-std=std,panic_abort`, because a prebuilt WASM standard
|
||||
library was unavailable. The synthetic Arabic proof was exported to IDML and
|
||||
reopened in the desktop app: preflight reported zero errors and zero warnings.
|
||||
The original Chinese IDML was also reopened; its four Mojikumi/kinsoku-priority
|
||||
compatibility warnings remain, along with separate font/link/overset findings.
|
||||
|
||||
- [Unicode UAX #9](https://www.unicode.org/reports/tr9/) defines paragraph resolution
|
||||
and line-level bidirectional ordering.
|
||||
- [Adobe Middle Eastern scripting guide](https://developer.adobe.com/indesign/uxp/resources/recipes/rtl/)
|
||||
documents the distinct direction, digit, kashida and diacritic controls.
|
||||
- [Diacritic position enumeration](https://developer.adobe.com/indesign/uxp/dom/api/d/diacritic-position-options/)
|
||||
and [positional forms](https://developer.adobe.com/indesign/uxp/dom/api/p/positional-forms/)
|
||||
identify serialized options; their presence alone does not establish visual parity.
|
||||
@@ -0,0 +1,64 @@
|
||||
# CJK import and composition
|
||||
|
||||
Task: CJK1. This is incremental support, not a claim of InDesign composition parity.
|
||||
|
||||
## Data flow
|
||||
|
||||
The IDML adapter resolves font resources and named/custom kinsoku sets into portable
|
||||
document data. `doc::cjk` owns those resources; `CharAttrs` and `ParaAttrs` retain
|
||||
inheritance and local overrides. The composer resolves composite-font character
|
||||
assignments before shaping, applies character metrics, then supplies break
|
||||
constraints to both line breakers. Line placement applies em alignment, leading
|
||||
references and hanging punctuation. The renderer consumes placed glyphs unchanged.
|
||||
|
||||
`style.compositeFont.list` and `style.compositeFont.set` expose resources through the
|
||||
existing command channel. Editing uses normal document history and style cache
|
||||
invalidation. Font listing, replacement and preflight resolve component fonts.
|
||||
|
||||
## Implemented behavior
|
||||
|
||||
| Rule | Behavior and coverage |
|
||||
| --- | --- |
|
||||
| Composite fonts | Character mappings, component family/style, relative size, scales and baseline shift; synthetic selection and command/undo tests |
|
||||
| Leading/trailing aki | Explicit em spacing without outline scaling; inherited resets and IDML round trip |
|
||||
| Kerning import | Automatic methods take precedence over inactive numeric values; the 1e11 sentinel cannot become manual spacing |
|
||||
| Tsume | Compress nonnegative glyph sidebearings without scaling ink; full compression regression |
|
||||
| Jidori | Fixed group width with protected internal boundaries and justification advances; both composers tested |
|
||||
| Kinsoku sets | Custom character sets and independent common punctuation defaults for named sets; explicit disabling retained |
|
||||
| Bunri kinshi / rensuuji | Protect repeated dash/ellipsis pairs and numeric sequences |
|
||||
| Hanging punctuation | Regular/forced edge allowance integrated with line breaking and placement |
|
||||
| Ideographic spaces | Paragraph switch controls whether fullwidth spaces participate in word spacing |
|
||||
| Em alignment / leading | Em top/center/bottom alignment and baseline reference offsets; mixed-size regressions |
|
||||
| Kenten / glyph forms | Preserve emphasis character, shape it with font fallback; map supported OpenType forms |
|
||||
| Tate-chu-yoko offsets | Carry horizontal/vertical offsets through the existing vertical group placement |
|
||||
|
||||
## Unfinished behavior
|
||||
|
||||
- **Mojikumi**: definitions, base-set names, all override rows and paragraph references
|
||||
survive import/export. The numeric character-class spacing tables and their
|
||||
compression priorities are **not executed**. Preflight reports this explicitly.
|
||||
- **Kinsoku push-in/push-out priorities**: retained, but not used in candidate
|
||||
selection. Preflight reports this explicitly. Boundary prohibition alone is not
|
||||
equivalent to implementing this priority policy.
|
||||
- Named kinsoku defaults are clean-room common punctuation rules, not verified
|
||||
reproductions of every vendor preset. Explicit custom tables take precedence.
|
||||
- ICF alignment currently uses font ascender/descender metrics; exact ideographic
|
||||
character-face metrics are not implemented. CenterDown currently shares the
|
||||
center leading reference. These require separate compatibility work.
|
||||
- Jidori groups are scoped to resolved shaping runs. Mixed component-font or style
|
||||
changes inside one intended group need a group model above shaping.
|
||||
- Frame-grid composition and detailed ruby/warichu controls are outside this change.
|
||||
|
||||
Preserving a parameter is not evidence that its visual rule has been implemented.
|
||||
Missing fonts, missing links and overset remain separate preflight findings;
|
||||
removing unsupported-rule warnings to produce a clean report is not acceptable.
|
||||
|
||||
## References
|
||||
|
||||
- [Adobe MojikumiTable API](https://developer.adobe.com/indesign/uxp/dom/api/m/mojikumi-table/)
|
||||
describes the serialized override tuple, but does not define the numeric class mapping.
|
||||
- [Adobe spacing priorities](https://helpx.adobe.com/hk_en/indesign/desktop/language-and-proofing/chinese-japanese-and-korean/set-spacing-priorities-in-mojikumi-character-classes.html)
|
||||
describes ordered compression, which requires more than loading the table.
|
||||
|
||||
Tests use synthetic documents; user IDML files and proprietary application output
|
||||
are not committed as fixtures.
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
# CJK typesetting plan
|
||||
|
||||
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
|
||||
DesignCraft 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](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:
|
||||
|
||||
- W3C *Requirements for Japanese Text Layout* (JLREQ): character classes, line composition,
|
||||
spacing (aki), line breaking (kinsoku), ruby, kenten, warichu, tate-chu-yoko, grids.
|
||||
- W3C *Requirements for Chinese Text Layout* (CLREQ) and *Korean* (KLREQ): punctuation placement
|
||||
for Simplified and Traditional Chinese, Korean word-based line breaking.
|
||||
- JIS X 4051 (published standard; JLREQ follows it).
|
||||
- OpenType: `vmtx`/`vhea`/`VORG`, `BASE`, the `vert`/`vrt2`/`vkrn`/`vpal`/`palt`/`halt`/`vhal`,
|
||||
`locl`, `ruby`, width (`fwid`/`hwid`/`pwid`/`qwid`/`twid`) and form (`jp78`/`jp83`/`jp90`/`jp04`,
|
||||
`trad`, `expt`, `nlck`, `hojo`) features.
|
||||
- The published IDML specification for attribute names and values.
|
||||
|
||||
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`); DesignCraft-only, not in IDML |
|
||||
| Mojikumi | tables and paragraph references kept through IDML; not applied (Preflight says so) |
|
||||
| Aki, tsume, jidori | done ([cjk-typography.md](cjk-typography.md)) |
|
||||
| Character alignment, leading model | em box top / centre / bottom, ICF from ascender and descender, roman baseline; aki above / below, centre (centre down as centre) |
|
||||
| Tate-chu-yoko | manual, with offsets; no auto, 3+ digits overflow the em |
|
||||
| Ruby | group ruby only, fixed 50 % size, no options, doesn't affect leading |
|
||||
| Kenten | the IDML kinds and a custom character, drawn as characters; no position, size, alignment, colour or font |
|
||||
| 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 |
|
||||
| Everything else below | missing |
|
||||
|
||||
Known bugs to fix first:
|
||||
|
||||
- A justified CJK line has no stretch except Single Word Justification.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Character classes
|
||||
|
||||
A `cjk::class_of(char, &Context) -> Class` in `designcraft-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
|
||||
|
||||
- Kinsoku sets (`doc::cjk::Kinsoku`: characters not allowed at line start, at line end, that may
|
||||
hang, and inseparable pairs), the paragraph's set, hanging (none / regular / force),
|
||||
bunri-kinshi and rensuuji are in place: they constrain breaks in both breakers. Remaining: the
|
||||
built-in hard and soft sets from JLREQ's classes (today's named sets are common punctuation
|
||||
lists), and the kinsoku type (push in first / push out first / push out only / prioritise the
|
||||
adjustment amount), which needs the Japanese composer's push-in and push-out candidates.
|
||||
- A `Composer::Japanese` (paragraph) and `Composer::JapaneseSingleLine` beside the Western
|
||||
composers. In `breaker::items` the Japanese composer emits:
|
||||
- boxes per glyph,
|
||||
- mojikumi glue between glyphs with priority tiers (compress punctuation first, then
|
||||
expand inter-ideograph space, then Western/CJK spacing, then letter-spacing),
|
||||
- kinsoku penalties (infinite inside prohibited pairs; push-in candidates scored by
|
||||
compression needed),
|
||||
- hanging punctuation as zero-width boxes allowed past the measure.
|
||||
Knuth–Plass already takes tiered stretch (`TIER_COST`), so this plugs into `kp_pass`.
|
||||
- Justification in `layout_line` distributes by the same tiers; the last line follows the
|
||||
paragraph's last-line alignment; CJK lines justify by default (JLREQ).
|
||||
- Korean: the Japanese composer with word-based breaking (spaces) by default; per-paragraph
|
||||
option for character-based breaking.
|
||||
|
||||
### Vertical text, properly
|
||||
|
||||
- Read `vmtx`, `vhea`, `VORG` and the ideographic em box (`BASE` `ideo`/`idtp`, else OS/2 typo
|
||||
metrics). `FontFace` gets `v_advance(gid)`, `v_origin(gid)` and `em_box()`.
|
||||
- Shape upright runs with direction `TopToBottom` so `vert`, `vkrn` and `vpal` apply and the
|
||||
shaper reports vertical advances; rotated (Latin) runs stay horizontal and turn as a whole.
|
||||
`vrt2` only for runs that are rotated, never with an extra turn.
|
||||
- Glyph placement in vertical lines uses the vertical origin, not a guessed centre.
|
||||
- Story direction per story (IDML `StoryOrientation`); frames of one story agree.
|
||||
- Arrow keys and carets follow the vertical line direction.
|
||||
|
||||
### Annotations and inline structures
|
||||
|
||||
All set after line layout like today's ruby, but able to affect line height:
|
||||
|
||||
- **Ruby**: per-character (mono), group and jukugo ruby; alignment (1-2-1, centre, left/right,
|
||||
equal space, JIS rule), position (above/right, below/left), size, font, colour, x/y scale,
|
||||
offsets, overhang onto neighbours (none / ½ / 1 ruby character / ½ base / unlimited), automatic
|
||||
scaling, spacing to the base, "ruby adjusts leading".
|
||||
- **Kenten**: position, size, alignment, colour, font (the kinds and a custom character are
|
||||
there).
|
||||
- **Warichu**: an inline run set in 2+ smaller lines inside one line height, with size, line
|
||||
spacing, alignment and breaking rules.
|
||||
- **Tate-chu-yoko**: scale-to-fit-em, automatic TCY for runs of N digits (with or without
|
||||
Latin), rensuji (offsets are there).
|
||||
- **Shatai** (oblique in the CJK sense: angle and magnification, keeping the em box),
|
||||
**character rotation**.
|
||||
- **Character alignment and leading model**: ICF top and bottom from the font's ideographic
|
||||
character face (`BASE` `icft`/`icfb`) rather than its ascender and descender, and centre down
|
||||
as its own model.
|
||||
|
||||
Jidori, tsume, aki before and after, em-box alignment and the other leading models are in place
|
||||
([cjk-typography.md](cjk-typography.md)).
|
||||
|
||||
### Grids
|
||||
|
||||
- **Layout grid** (per page / parent): character size, characters per line, lines per column,
|
||||
columns and gutter, origin; drawn like the baseline grid; snap target.
|
||||
- **Frame grids**: text frames that show a character grid (horizontal or vertical), with a
|
||||
character count, named grids as document resources (font, size, scale, aki between characters
|
||||
and lines, alignment, view options), grid alignment of paragraphs (em-box centre, ICF top/bottom,
|
||||
roman baseline, gyoudori: lines spanning N grid lines), grid tracking.
|
||||
- Frame Grid tools (horizontal and vertical) and Object › Frame Type (text frame ↔ frame grid).
|
||||
|
||||
### Fonts
|
||||
|
||||
- **Composite fonts** (a named font made of a base font plus fonts for character sets, with size,
|
||||
baseline, x/y scale and centre adjustments) are resolved before shaping, read from and written
|
||||
to IDML and listed, replaced and preflighted by their member fonts; PDF embeds the member fonts.
|
||||
Remaining: the Composite Fonts dialog, and the predefined character classes (kanji, kana,
|
||||
full-width symbols, punctuation, Latin, numerals) beside custom sets.
|
||||
- **Missing glyphs**: InDesign doesn't substitute fonts per character: a character the applied
|
||||
font lacks shows as the font's missing-glyph box, prints that way and is a Preflight error.
|
||||
DesignCraft does the same, with a document setting (Draw Missing Glyphs from Fallback Fonts)
|
||||
that draws such characters from fallback fonts instead.
|
||||
- **Language-aware fallback** (when that setting is on): Japanese, Simplified Chinese, Traditional
|
||||
Chinese and Korean each have their own fallback chain of system fonts (Japanese: the craft-fonts
|
||||
Japanese faces first); `locl` follows the character language (IDML language names).
|
||||
- **No CJK fonts in the repo.** CJK fonts are large (15–25 MB each even subset) and documents
|
||||
should name the fonts they use. The Japanese UI and document faces come from the optional
|
||||
craft-fonts build input when DesignCraft is built with it (release builds are).
|
||||
- **Document fonts**: fonts in a `Document Fonts` folder beside an opened `.designcraft` or
|
||||
`.idml` file are available to that document only, ahead of system fonts, and unloaded when it
|
||||
closes. File › Package copies the document's fonts into the package's `Document Fonts` folder
|
||||
(font licences permitting: OS/2 `fsType` restricted-licence fonts are listed, not copied), so a
|
||||
packaged CJK document opens with its fonts on another machine. On the web, the fonts of a
|
||||
dropped or opened package folder load the same way.
|
||||
|
||||
### Editing and output
|
||||
|
||||
- Find/Change: kana-sensitive and width-sensitive matching; full/half-width and
|
||||
hiragana/katakana conversion (Type › Change Case).
|
||||
- Numbering styles: kanji, full-width, circled, parenthesised.
|
||||
- Typographer's quotes per language (Simplified/Traditional Chinese, Korean).
|
||||
- Character count (Info panel) by CJK conventions.
|
||||
- EPUB/HTML: `writing-mode: vertical-rl`, `<ruby>`, `text-combine-upright`, `text-emphasis`.
|
||||
- PDF: correct `ActualText` for ruby/warichu; vertical glyph placement as above.
|
||||
- IDML: every attribute below, import and export, round-trip tested.
|
||||
|
||||
## 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`, `RubyFlag`, `RubyString`,
|
||||
`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: `KentenFont`, `KentenFontStyle`, `KentenSize`, `KentenPosition`, `KentenAlignment`,
|
||||
`KentenCharacterSet`, `KentenXScale`, `KentenYScale`, `KentenFillColor`/`Tint`, `RubyType`,
|
||||
`RubyAlignment`, `RubyPosition`, `RubyFont`, `RubyFontStyle`, `RubyFontSize`, `RubyXScale`,
|
||||
`RubyYScale`, `RubyXOffset`, `RubyYOffset`, `RubyOverhang`, `RubyParentSpacing`,
|
||||
`RubyParentOverhangAmount`, `RubyAutoAlign`, `RubyAutoScaling`, `RubyAutoTcyDigits`, `Rensuji`,
|
||||
`CharacterRotation`, `ShataiMagnification`, `ShataiDegreeAngle`, `ShataiAdjustRotation`,
|
||||
`ShataiAdjustTsume`, `Warichu`, `WarichuLines`, `WarichuSize`, `WarichuLineSpacing`,
|
||||
`WarichuAlignment`, `WarichuCharsBeforeBreak`, `WarichuCharsAfterBreak`.
|
||||
|
||||
Paragraph: `Composer` (the Japanese composers), `GridAlignment` (all values),
|
||||
`GridAlignFirstLineOnly`, `GridGyoudori`, `ParagraphGyoudori`, `CjkGridTracking`, `AutoTcy`,
|
||||
`AutoTcyIncludeRoman`, `RotateSingleByteCharacters`, `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 `designcraft_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. DesignCraft 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.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Contributors in the About window
|
||||
|
||||
**About ▸ Contributors** credits everyone who contributed to DesignCraft, and **About ▸ Models** credits
|
||||
the AI models named in `Co-Authored-By` trailers. This follows the shared craftrules standard
|
||||
[`standards/contributors.md`](https://github.com/storytold/craftrules/blob/main/standards/contributors.md);
|
||||
this page is the local copy of the decision.
|
||||
|
||||
## Decision
|
||||
|
||||
- Two files, both **compiled into the binary** by the UI crate's `build.rs` (nothing is read from
|
||||
disk or the network at run time; the web build has them too):
|
||||
- [`contributors/contributors.json`](../contributors/contributors.json): commit stats per GitHub
|
||||
username. **Generated** by `craftrules/scripts/contributors.py`; never hand-edit.
|
||||
- [`contributors/people.toml`](../contributors/people.toml): the names people chose for
|
||||
themselves. **Each contributor adds only their own entry**, in their own PR.
|
||||
- Each contributor gets one line: GitHub username (always), display name (opt-in, or their synced
|
||||
GitHub profile name), real name (only if they told us), merged PRs, commits, lines added, lines
|
||||
deleted, binary assets added, binary assets removed, first and last commit date.
|
||||
- The list is shown as a **grab bag** (names flowing as text) or a **table**, in the same order. It
|
||||
sorts by name, PRs, commits, lines added, lines deleted, line delta, binary assets added/removed,
|
||||
first or last commit date. The name toggle cycles **Username → Display name → Real name**; a
|
||||
missing name falls back to `@username`. Alphabetical sorting is case-insensitive and ignores the `@`.
|
||||
|
||||
## In DesignCraft
|
||||
|
||||
- The About window is `crates/ui-egui/src/about.rs` (Help ▸ About DesignCraft); `help.about {"tab":"contributors"}` opens a tab.
|
||||
|
||||
## Credit yourself
|
||||
|
||||
Add your entry to `contributors/people.toml` in a PR you commit yourself (every field optional):
|
||||
|
||||
```toml
|
||||
[people.your-github-username]
|
||||
real_name = "Your Name"
|
||||
display_name = "Nickname"
|
||||
sync_github_name = true # use the name on your public GitHub profile as display name
|
||||
```
|
||||
|
||||
or, with craftrules checked out next to `craft-apps/`:
|
||||
|
||||
```sh
|
||||
python3 ../../craftrules/scripts/contributors.py --add-me . --real-name "Your Name" --sync-github-name
|
||||
```
|
||||
|
||||
Names show after the next refresh: the script checks that each entry was committed by its own
|
||||
user (or by a maintainer with a `consent_source`). Delete your entry to remove your names.
|
||||
|
||||
## Refreshing (maintainers)
|
||||
|
||||
```sh
|
||||
python3 ../../craftrules/scripts/contributors.py . # regenerate + verify; needs git, Python 3.11+, gh
|
||||
python3 ../../craftrules/scripts/contributors.py --check . # verify people.toml only
|
||||
```
|
||||
|
||||
Commit the updated `contributors/contributors.json`. Git author names and emails are never recorded.
|
||||
@@ -3,6 +3,16 @@
|
||||
Start the app with `--control <port>` (or `DESIGNCRAFT_CONTROL_PORT`). The server listens on
|
||||
`127.0.0.1` only and speaks JSON lines: one request object per line, one reply per line.
|
||||
|
||||
**Only requests are read.** Every line must be a JSON object with a string `method` (`id` and `params`
|
||||
are optional; blank lines are skipped). Anything else gets one error reply
|
||||
(`{"ok": false, "error": "… closing the connection"}`) and the server **closes the connection**, so
|
||||
nothing sent after it on that connection runs. That covers text that isn't JSON, a JSON array or number,
|
||||
an object without `method`, invalid UTF-8, and a line longer than 4 MiB. An HTTP request (for example a
|
||||
web page's cross-origin `fetch` to `127.0.0.1:<port>`) therefore can't smuggle a command in its body:
|
||||
its request line is rejected first. At most 16 connections are served at once; further ones get an error
|
||||
line and are closed. Clients that get an error reply should reconnect. The port has no authentication,
|
||||
so only enable it while you use it. Transport: `apps/designcraft/src/control_server.rs`.
|
||||
|
||||
```json
|
||||
{"id": 1, "method": "engine.execute", "params": {"command": "frame.create", "params": {"rect": [36, 36, 300, 200], "content": "text"}}}
|
||||
{"id": 1, "ok": true, "result": {"id": 10, "story": 11}}
|
||||
@@ -18,7 +28,7 @@ Start the app with `--control <port>` (or `DESIGNCRAFT_CONTROL_PORT`). The serve
|
||||
| `ui.tool.select` | `{tool}` | Select a tool (`selection`, `type`, `rectangleFrame`, …) |
|
||||
| `ui.pointer` | `{events:[{kind: down\|drag\|up\|move\|doubleclick, x, y, space?: "screen"\|"canvas"}], mods?}` | Drive the active tool through the same code path as the mouse |
|
||||
| `ui.key` / `ui.text` | `{key, shift?, alt?, cmd?}` / `{text}` | Synthetic keyboard input (typing into a text frame) |
|
||||
| `ui.move` / `ui.click` / `ui.drag` | screen points | Real egui pointer input — reaches every widget, menu and panel |
|
||||
| `ui.move` / `ui.click` / `ui.drag` | screen points, `button?: left\|right\|middle` | Real egui pointer input — reaches every widget, menu and panel |
|
||||
| `ui.set` | `{brightness?, panel?, rulers?, guides?, frameEdges?, baselineGrid?, textThreads?, screenMode?, zoom?, page?, fit?}` | UI state |
|
||||
| `ui.dialog.open` | `{id, fields?}` | Open a dialog by id (e.g. `paragraphStyleOptions` with `{name, section}`) |
|
||||
| `ui.dialog.set` / `ui.dialog.confirm` / `ui.dialog.cancel` | `{field, value}` | Fill and confirm the open dialog |
|
||||
|
||||
+1
-1
@@ -93,4 +93,4 @@ key {"key": "Escape"}
|
||||
|
||||
## Community links
|
||||
|
||||
`app.links` returns the ArtCraft Discord (https://discord.gg/artcraft), the website (https://getartcraft.com), DesignCraft's app page (https://getartcraft.com/apps/designcraft), its GitHub repository (https://github.com/storytold/designcraft) and the issue tracker. In the running app, the UI commands `help.discord`, `help.appPage`, `help.github`, `help.issues`, `help.website`, `help.app {app}` open them in the browser and `help.about` shows the About splash. The server's `serverInfo.websiteUrl` is the app page.
|
||||
`app.links` returns the ArtCraft Discord (https://discord.gg/artcraft), the website (https://getartcraft.com), DesignCraft's app page (https://getartcraft.com/apps/designcraft), its GitHub repository (https://github.com/storytold/designcraft) and the issue tracker. In the running app, the UI commands `help.discord`, `help.appPage`, `help.github`, `help.issues`, `help.website`, `help.app {app}` open them in the browser and `help.about {tab?}` shows the About window (tabs `about`, `contributors`, `models`). The server's `serverInfo.websiteUrl` is the app page.
|
||||
Reference in new issue
Block a user