Commit Graph
47 Commits
Author SHA1 Message Date
Haseom Shin 07b95eb7b3 Compose: don't panic when a hyphenated paragraph starts with a no-break letter (#115) 2026-10-10 01:09:36 -04:00
aiongg 99f1e653f2 Text: locale-coded language names (de_DE_2006) get their language
IDML names some languages by locale code
(AppliedLanguage="$ID/de_DE_2006", "$ID/nl_NL_2005") beside names such
as "English: USA" and "Spanish: Castilian". language_tag maps any locale
code (ll, ll_CC, ll-CC, with a reform year after them) to its BCP 47 tag
(de-DE, nl-NL; kept once per process, at most 512 distinct tags), ignores
IDML's $ID/ prefix, and knows Spanish: Castilian (es-ES) and Swiss French
and Italian. The stored name stays as written, so IDML round trips are
unchanged.

What depends on the language now goes by the tag: typographer's quotes
(by language subtag; Swiss regions take guillemets), English
hyphenation and spelling (is_english), Korean word breaking and the CJK
fallback chains (ja-JP, ko-KR are Japanese and Korean whatever the
region).
2026-10-07 10:44:46 -04:00
aiongg 1ef187ad02 Text: missing glyphs show as boxes
A character the applied font lacks is drawn as that font's missing-glyph
box (its .notdef, gid 0), on screen and in PDF, and Preflight lists the
characters by font ("Missing glyphs in <family> <style>: …", an error,
at most 20 named). A .notdef without an outline is drawn as a hollow box
across its advance, so a missing character never disappears. PDF draws
.notdef glyphs as paths, which PDF/A and PDF/UA accept. Kenten marks are
set in their text's font the same way.

The document setting Draw Missing Glyphs from Fallback Fonts
(DocSettings::glyph_fallback; Preferences > Composition,
document.preferences {glyphFallback}, undoable) draws them from fallback
fonts instead (the language-aware chains, then any font; kenten marks
from a fallback font, else a bullet). It is off in new documents and
IDML imports and on in files saved before it existed, which keep
drawing as they did. A missing font's substitute always uses fallback
fonts. The composition cache's signature includes the setting. Tests
whose Arabic comes from a fallback font turn the setting on.
2026-10-07 10:44:46 -04:00
aiongg 5f52150b31 CJK 1.9: document fonts belong to their document
The fonts in a document's Document Fonts folder are registered under a
font scope that the document holds while it is open (Document::font_scope,
runtime only; DocState keeps the scope alive and closes it when the last
copy of the state goes: file.close, closing a tab, revert, a book
chapter's export, the session ending). Composition, preflight, font.list,
Package and the font menus, Glyphs panel and previews look fonts up
through FontDb::scoped(scope): the document's own faces first, then the
shared ones. Other open documents don't see them, and two documents with
fonts of the same name each set text in their own. Composite fonts'
member fonts and the fallback for kenten marks are looked up the same
way.

Font files stay in memory for the session (FaceRef keeps faces for the
process); a document opened again reuses the faces of its unchanged files
(same path, length and modification time) instead of reading them again.
The composition cache's signature includes the scope; the word cache and
the outline and PDF font caches are keyed by face. Variable font
instances are kept per base face.

A family's installed faces join the shared fonts all at once, and a
face two lookups load at the same time is kept once, so a parallel
fallback lookup never takes a family's bold before its regular has
loaded.
2026-10-07 10:44:46 -04:00
aiongg 6df33fa344 CJK 1.7: story direction belongs to the story
Vertical is a story property: every frame of a thread is vertical or
horizontal together, a frame threaded onto a story takes its direction,
and frames split off by threading or breaking a thread keep theirs.
Type > Story Direction is type.storyDirection {vertical} (checked in the
menu) for the stories of the text selection or selected frames;
object.textFrameOptions {vertical} and frame.create {vertical} set the
frames' stories. Documents that stored Vertical on the frame open with
each story taking its first frame's direction. IDML StoryOrientation is
read into and written from the story, beside its column direction
(StoryDirection), which stays a separate setting.
2026-10-07 10:44:46 -04:00
aiongg 1574dcdd45 CJK 1.6: upright text is shaped top to bottom
In vertical frames upright runs are shaped TopToBottom (vert; vkrn/vpal
when asked), advance by their vertical metrics and hang from their
vertical origins on the em box centre, on screen and in PDF; their line
ascent and descent are the em box. Turned runs stay horizontal without
vrt2 (rather than every run taking vert and vrt2), and tate-chu-yoko
stays horizontal and centres on the em box, its offsets moving it along
and across the line. Horizontal Kana in upright runs takes vkna. Each
run is shaped with the text around it, as horizontal runs are.
2026-10-07 09:33:52 -04:00
aiongg e0fb78a230 CJK 1.4: Korean breaks at spaces
Korean is set word by word (KLREQ): Hangul, in any language, and Han
ideographs in Korean-language text take no line breaks between
characters, so lines break at spaces. Glyphs carry their language tag
for this. A paragraph attribute, koreanCharBreaks (type.para, paragraph
styles, Paragraph Style Options > Justification), breaks between
syllables instead. It is DesignCraft-only; IDML doesn't carry it.
A paragraph's kinsoku set decides which breaks it forbids, and Korean
still breaks at spaces under one.

Kinsoku still holds, also when the single-line composer has to split a
word wider than the measure: the split moves back so no line starts
with closing punctuation.
2026-10-07 09:33:52 -04:00
Shao Wenbin 02c5f61173 AR1.1: verify Kashida fallback without Tatweel fonts 2026-10-07 19:37:03 +08:00
Shao Wenbin c861a9f75e TYPE1: Reapply Chinese and Arabic typesetting rules on main
Port the CJK1 and AR1 changes from 3ecdb78 and de83cdc onto current main. Resolve translation and font-stack conflicts by retaining main localization and craft-fonts integration while preserving all typesetting code, tests, and documentation.

Validation: all six CI gates passed, including 616 tests and WASM checks.
2026-10-07 18:16:32 +08:00
Brandon ThomasandClaude Opus 5.5 a539c76632 Never crash: doc and compose
expect() calls become let-else / fallbacks. A parent spread without pages
(possible in a .designcraft file) no longer underflows in parent_page_for,
and check() rejects a next_id so large that allocating ids would overflow.
Both crates deny the panicking lints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:43:33 -04:00
Brandon ThomasandClaude Opus 5.5 5294c8f677 T: ruby and kenten
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:47:55 -04:00
Brandon ThomasandClaude Opus 5.5 5b7969901c T: right-to-left carets and selections; Binding: Right to Left
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:34:47 -04:00
Brandon ThomasandClaude Opus 5.5 2827574a47 T: kashida justification for Arabic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:15:38 -04:00
Brandon ThomasandClaude Opus 5.5 fe40d4b80b T: tate-chu-yoko in vertical frames
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:46:52 -04:00
Brandon ThomasandClaude Opus 5.5 a57a909db1 T: vertical punctuation forms (vert) in vertical frames
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:35:02 -04:00
Brandon ThomasandClaude Opus 5.5 79f266c04c T: text language — hyphenation, spelling and quotes follow it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:01:50 -04:00
Brandon ThomasandClaude Opus 5.5 55af964ea8 X: inline XML tagging and tag markers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:48:58 -04:00
Brandon ThomasandClaude Opus 5.5 77fc006b02 T: vertical type — Vertical Type tool, upright CJK, CJK line breaking
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:18:14 -04:00
Brandon ThomasandClaude Opus 5.5 6b2fa19f00 T: right-to-left text — bidi shaping and line reordering, paragraph direction
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 05:29:56 -04:00
Brandon ThomasandClaude Opus 5.5 a4e76ed620 T: Track Changes — marked insertions and deletions, accept/reject all
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:57:22 -04:00
Brandon ThomasandClaude Opus 5.5 2fefab232c T: custom anchored object positions (+ IDML)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:55:34 -04:00
Brandon ThomasandClaude Opus 5.5 79c73c815d T: paragraph borders and shading offsets, per column part (+ panel, IDML)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:23:49 -04:00
Brandon ThomasandClaude Opus 5.5 4f18835eb3 T: hyphenation exceptions in the user dictionary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:06:37 -04:00
Brandon ThomasandClaude Opus 5.5 ee89bf187f T: nested line styles (+ dialog, IDML)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:01:48 -04:00
Brandon ThomasandClaude Opus 5.5 d11b23d805 T: endnotes — numbered references, generated endnote frame, options
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:10:36 -04:00
Brandon ThomasandClaude Opus 5.5 af0d241190 T: conditional text — conditions, hide/show in layout and exports, panel, IDML
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:02:15 -04:00
Brandon ThomasandClaude Opus 5.5 98d8edf764 T: Composition highlights — keep and H&J violations, custom tracking/kerning
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:35:23 -04:00
Brandon ThomasandClaude Opus 5.5 53ec1e046c T: Preferences › Advanced Type (super/subscript size and position)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:28:38 -04:00
Brandon ThomasandClaude Opus 5.5 1b778945af T: named lists continuing across stories (list.define, listName/startAt); null clears overrides
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:52:11 -04:00
Brandon ThomasandClaude Opus 5.5 eb4fa9c64c T: Type on a Path — tool, options, screen and PDF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:34:20 -04:00
Brandon ThomasandClaude Opus 5.5 fcef655ba8 T: nested styles and GREP styles (compose overlays, Paragraph Style Options); ui.dialog.open
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:47:52 -04:00
Brandon ThomasandClaude Opus 5.5 4aa76b85f9 T: Underline / Strikethrough Options — weight, offset, colour, tint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 15:57:02 -04:00
Brandon ThomasandClaude Opus 5.5 69563933be M2: Find/Replace Font and missing-font highlighting
- engine: font.list (fonts used, missing first), font.replace (text, cells, footnotes, styles) (+ test)
- compose: RunStyle.missing_font (memoized per composition); render: pink highlight behind such
  text when RenderOptions.highlight_missing_fonts (canvas only, never output) (+ test)
- UI: Type › Find/Replace Font… dialog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:47:12 -04:00
Brandon ThomasandClaude Opus 5.5 5a9fa2d16a M5: anchored objects — inline and above line
- doc: OBJECT_MARK with Story::objects (items normalized to the origin) kept in sync on edits,
  copy/paste slices and threading
- compose: inline objects are wide glyphs sitting on the baseline (Y offset; auto leading grows);
  above-line objects push their line down; placements per frame follow vertical justification
- render + PDF draw anchored items with their lines
- engine: anchored.insert/options/release/list; pasting copied items into text anchors them;
  Options/Release act on the object at the insertion point (+ test)
- Object › Anchored Object menu

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:21:24 -04:00
Brandon ThomasandClaude Opus 5.5 01b3603751 M7: cross-references and text anchors; menus expose engine features
- doc: ANCHOR_MARK / XREF_MARK with Story::anchors / xrefs kept in sync on edits, 11 formats
  (InDesign's building blocks + partial paragraph), paragraph anchors, typographic quotes
- compose: anchor pages via an XrefIndex (cached per document signature); cross-references are
  resolved at composition, so they never go stale; '??' for missing destinations
- PDF: cross-references become link annotations to the destination page
- IDML: export/import as HyperlinkTextDestination / CrossReferenceSource / Hyperlink /
  CrossReferenceFormat (+ XRef page variables); verified live in InDesign 2026
- engine: xref.insert/list/setFormat/defineFormat/formats, anchor.create/list (+ tests)
- UI: New Cross-Reference dialog (styles → paragraphs, format); menu pass adding TOC, sections,
  text variables, hyperlinks, footnotes, step and repeat, spelling, fitting/content submenus,
  effects, Data Merge, Preflight, EPUB; fixed-parameter menu variants; ui.menu.invoke opens
  generated dialogs; every-menu-entry-is-a-command test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:26:47 -04:00
Brandon ThomasandClaude Opus 5.5 3ad71d5a76 M7: footnotes — Insert Footnote, Document Footnote Options, column-bottom layout, PDF/IDML; generic command dialogs
- doc: FOOTNOTE_REF marker + Story::notes (kept in sync on insert/delete/thread), FootnoteOptions,
  NumberStyle::Symbols, footnote text addressed like cell text (CellAddr::footnote)
- compose: notes numbered (continuous / restart per page, spread, section), composed at column
  width, body text makes room, stacked at the column bottom with the rule above
- render + PDF draw footnote text; IDML export/import in InDesign's structure (verified in
  InDesign 2026), FootnoteOption preferences
- engine: footnote.insert/options/list/setText/delete/goToReference (+ tests)
- UI: caret/selection in footnote text, Footnote Options dialog, Type menu entries; menu items
  labelled '…' with parameters open a dialog generated from the command's params doc
- sample magazine carries a footnote

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:01:27 -04:00
Brandon Thomas 2f474bfb4d Merge branch 'worktree-agent-a97a3bd20b71c4f73'
# Conflicts:
#	crates/compose/src/lib.rs
#	crates/compose/src/shape.rs
#	crates/doc/src/lib.rs
2026-10-01 12:55:23 -04:00
Brandon Thomas b4c52c249b continued work 2026-10-01 12:51:28 -04:00
Brandon ThomasandClaude Opus 5.5 7a7d7b78a1 Merge main into M8 tables
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:47:48 -04:00
Brandon ThomasandClaude Opus 5.5 77a23151ba M8: tables
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:44:45 -04:00
Brandon ThomasandClaude Opus 5.5 35eb84d629 fix clippy (FaceRef is Copy)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:41:38 -04:00
Brandon ThomasandClaude Opus 5.5 8e4403af38 M9: parallel composition scales (Copy FaceRef instead of per-glyph Arc clones; BMP coverage bitset; read-locked word cache)
Cold compose of 1M chars on 8+ threads: ~255 ms -> ~38 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:41:29 -04:00
Brandon ThomasandClaude Opus 5.5 df7f506b05 M7: Table of Contents (generate/update, TOC Title/Level styles, page names via composition); tab leaders in the composer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:32:59 -04:00
Brandon ThomasandClaude Opus 5.5 6086ceb497 compose: column/frame breaks re-flow the rest of the paragraph into the next column/frame; tab tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:07:08 -04:00
Brandon ThomasandClaude Opus 5.5 83915e2fc2 M2.5: dictionary hyphenation, letter/glyph justification, keeps
- hyphen: bundled en-US dictionary from the public-domain Moby Hyphenator II
  list (Grady Ward, Gutenberg #3204), front-coded + deflated (433 KB), with
  case/apostrophe/punctuation/compound handling and inflection inheritance;
  our own Liang patterns (19.5k, 129 KB) trained by a patgen-style trainer
  (hyphen::patgen, examples/hyphgen.rs) for unknown words; heuristic last.
  Discretionary hyphens replace automatic points.
- breaker: prioritised word/letter/glyph-scaling glue in Knuth-Plass and
  layout; uncapped badness; hyphen limit as a hard constraint (hyphen-count
  states); hyphenation zone for ragged text (both composers); ragged
  hyphenation measured correctly; optical margin alignment; balance ragged
  lines; Knuth's node pruning.
- compose: Keep Options (keep with next, keep all lines, keep first/last)
  via paragraph snapshots; desired spacing applied to all alignments;
  justified last lines shrink instead of overflowing; word shaping and
  hyphenation caches (300k-char story: ~40 ms warm, ~60 ms cold).
- tests: golden corpus, perf (ignored), keeps, balance, optical, zone,
  limit, letter/glyph scaling, discretionary hyphens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:04:43 -04:00
Brandon ThomasandClaude Opus 5.5 91ae2b3cc9 M0: engine, tools, egui UI, desktop app, CLI, sample document, docs
Engine with command registry (~100 commands), tool hosting with one-step
undo per gesture, InDesign-style egui frontend (app bar, Control panel,
Tools panel, rulers, guides, frame edges, ports/threads, Properties/Pages/
Layers dock, panels, dialogs), JSON control channel, headless CLI renderer,
generated sample magazine, README/AGENTS/ROADMAP/ASSETS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:39:15 -04:00
Brandon Thomas c9dcad1f6e continued work 2026-10-01 09:36:32 -04:00