A Flex Layout container is a frame holding its objects as nested items
(as Edit › Paste Into makes), with a `flex` record: direction, wrap,
justification, cross-axis alignment, Fixed/Auto width and height, padding
and horizontal/vertical spacing. designcraft_doc::flex lays the objects
out (a Flexbox subset: lines, justification, alignment, Auto sizing), and
the engine runs it after every edit, so the container keeps its objects
in place whatever changes.
Commands: flex.add (Add to Flex Layout; detects aligned objects: their
direction, Auto size, Space Between at their own spacing, as InDesign
does), flex.convert (a group or rectangle frame), flex.remove (objects
stay, grouped), flex.set (every panel setting, plus the alignment grid
cell), flex.reorder. Resizing a container by its handles or W/H changes
its box and makes that side Fixed instead of scaling its objects;
dragging an object inside one drops it in the nearest place.
UI: Object › Flex Layout (Add to, Convert to, Remove, Show Flex Controls),
a Flex Layout panel (Window › Panels) laid out as InDesign's (Create Flex
Layout; alignment grid, W/H with Auto/Fixed; justification menu, four
directions, wrap; padding with the link button; spacing, greyed as
InDesign does when the justification works it out or nothing wraps), and
the same section in Properties. Icons are new drawings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add multi-click text selection, pending caret formatting and clipboard support, text-aware colour controls, guide/grid snapping, and frame threading with loaded-port interactions.
Include the layout, drop-cap and IDML updates carried from #83, with safeguards for generated and linked stories, external text references, local IDs, locked frames and atomic colour edits.
Fixes#23.
Co-authored-by: Sean Hammond <sean.hammond@und.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A placed PDF, EPS or SVG under the Eyedropper matches attributes again,
as before, instead of failing with "no placed image under the point"
(Asset::is_raster, shared by the tool and color.sample). color.sample
hands its colour to object.color, which now colours selected text, the
text of selected frames ({text: true}), or the typing colour of an empty
story; a caret in existing text is refused before a swatch or undo step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Share duplicate file data while keeping distinct link paths independent, and leave unused assets out of saved documents.
Validation: all six cargo xtask ci gates passed on the merge candidate; 1,170 native tests passed and all 15 WASM packages checked. Independent regression checks cover updates, relinking, embedding, undo and save/reopen.
Task: #420
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 <noreply@anthropic.com>
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.
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.
A document keeps one data source, the placeholders that point at its
fields, and the last merge layout. Empty merge state is omitted from
the file. New documents and IDML imports start with that state empty.
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.
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>
- 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>
- doc: Story::extract / insert_story carry formats, footnotes, cross-references, index markers
and tables through copy and paste
- engine: edit.copy/cut on text keep a formatted slice; edit.paste uses it while the system
clipboard matches (else plain text, no smart quotes); edit.pasteWithoutFormatting (⇧⌘V);
type.changeCase (upper/lower/title/sentence, run formats kept)
- UI: canvas copy/cut/paste go through the engine; Type › Insert Special Character (Symbols,
Markers, Hyphens and Dashes, Quotation Marks, Other), Insert White Space, Insert Break
Character, Change Case; nested submenus in the menu parser (+ test)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- doc: INDEX_MARK with Story::index_refs kept in sync (topics 1–4 levels, sort keys, current
page / to end of story / next n paragraphs / suppressed / See / See also); index builder with
section headings, nested or run-in entries, merged page ranges; Document::index for updates
- engine: index.addReference (topic from the selection), index.list, index.generate, index.update
with Index Title / Section Head / Level 1–4 styles (+ tests)
- IDML: PageReference markers and the Index topic tree with See / See also cross-references,
both ways (verified in InDesign 2026)
- Layout › Index menu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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>
- 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>