Commit Graph
296 Commits
Author SHA1 Message Date
Brandon Thomas 0df9238c73 Merge PR #850 2026-10-14 12:56:09 -04:00
midasmatterhorn e9bda1f2d9 idml: cap IDML package part size on import instead of reading unbounded 2026-10-14 18:01:41 +03:00
moppeeer 5f00aa7bb3 Merge remote-tracking branch 'origin/main' into codex/gradient-strokes
# Conflicts:
#	crates/engine/src/cmd/style.rs
#	crates/idml/src/tests.rs
2026-10-14 10:33:30 +02:00
moppeeer 3f1eb00bb3 Merge branch 'codex/page-tool' into codex/page-tool-control-panel
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-14 09:38:16 +02:00
aiongg 2f6b62a831 Merge #84 (final head 9b7a0ec6): Text editing, snapping and text threading
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/edit.rs
#	crates/engine/src/cmd/style.rs
#	crates/engine/src/cmd/text.rs
#	crates/engine/src/cmd/thread.rs
#	crates/geom/src/corners.rs
#	crates/idml/src/import.rs
#	crates/idml/src/tests.rs
#	crates/mcp/src/backend.rs
#	crates/tools/src/select.rs
#	crates/tools/src/tests.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/control.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/toolbar.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-14 04:19:26 +00:00
0f4f382b0e feat(text): improve editing, snapping, and threading
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>
2026-10-13 21:41:46 -06:00
skylar taylor-barrick 9b7a0ec66d fix(text): preserve document state while threading and formatting
Merge current main into the text editing and layout work, retaining the original contributor commits. Preserve generated and linked stories, incoming text references, local IDs and locked frames when threading.

Keep pending formatting and text colours consistent, isolate deferred control clipboard input, and handle inherited IDML frame properties with bounded column allocations. Add focused state-preservation and round-trip regressions and correct the socket and swatch test fixtures.

M2; refs #84.
2026-10-13 19:54:50 -06:00
aiongg d0035c0722 Merge #864: Control panel for the Page tool (W/H, preset, orientation, Liquid Page Rule); Controlled by Parent; Show Parent Page Overlay
# Conflicts:
#	crates/idml/src/import.rs
#	crates/ui-egui/src/chrome.rs
#	crates/ui-egui/src/i18n.rs
2026-10-14 01:37:34 +00:00
moppeeerandClaude Opus 5.5 59b6125af1 feat(ui): Control panel for the Page tool; Controlled by Parent; Show Parent Page Overlay
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (1:02–
2:06, 13:47) sizes pages from the Control panel with the Page tool
selected: X/Y, W/H, the page-size preset ("US Business Card"),
orientation, Liquid Page Rule ("Controlled by Parent"), Objects Move with
Page and Show Parent Page Overlay. DesignCraft's Control panel showed the
(empty) object controls.

- ui: with the Page tool, the Control panel shows the selected page (or
  the page in view): the reference point (W/H, preset and orientation
  keep it in place via layout.pageSize's anchor), X/Y (read-only),
  W/H, the preset dropdown, portrait/landscape, Liquid Page Rule, Objects
  Move with Page (always on: pages don't move within spreads yet) and
  Show Parent Page Overlay.
- doc: LiquidRule::ControlledByParent (the parent page's rule, Off
  without one; resolved by liquid::effective_rule); Page.show_parent_overlay.
- engine: layout.pageOptions {pages, showParentOverlay}; liquid.pageRule
  takes controlledByParent.
- canvas: the parent page's size over a page with the overlay on.
- Liquid Layout panel and Control panel share InDesign's rule list
  (Off, Scale, Re-center, Object-based, Guide-based, Controlled by
  Parent); "US Business Card" preset; translations.

Builds on the Page tool PR (#862) for the selected page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:51:47 +02:00
aiongg 71309b993e Integration: cargo fmt 2026-10-13 22:40:00 +00:00
aiongg b14e5feae7 Integration: #839 previews keep the link's format and pixel size, share one buffer per preview, and preflight still reports their missing links 2026-10-13 22:35:59 +00:00
aiongg 588e83199b Merge #839: INDD graphics whose linked file is missing show the document's preview
# Conflicts:
#	crates/engine/src/cmd/interchange.rs
#	crates/idml/src/import.rs
#	crates/idml/src/tests.rs
2026-10-13 22:33:58 +00:00
aiongg e19b6b1fd9 Merge toc-dialog: Table of Contents dialog, entry options and IDML TOC styles (#755)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests_idml.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-13 20:44:30 +00:00
aiongg 129adfa429 Layout: Table of Contents dialog with entry styles, page number position and IDML TOC styles (#755) 2026-10-13 20:41:56 +00:00
aiongg 65f0310fbe Integration: cargo fmt 2026-10-13 20:08:19 +00:00
aionggandGovinda Roy 06f2648958 Integration: from #833, paragraph rules inherit unstated settings and style names may end in a space
A paragraph style or range that sets only some of a rule's settings (such
as its colour) takes the rest from its paragraph style chain. Nested, line
and GREP style references keep a trailing space when it is part of a
character style's name. The PR's anchored-object and Western leading
changes are not taken (next has its own).

Co-authored-by: Govinda Roy <govindaroy.ofc94@gmail.com>
2026-10-13 20:02:35 +00:00
aiongg 7f0d7f1f32 Merge t13-moppeeer: #253 #832 #753 #754 (a3b97165) #588
# Conflicts:
#	ROADMAP.md
#	crates/idml/src/tests.rs
2026-10-13 19:57:37 +00:00
aiongg 82c94c7270 Integration: cargo fmt 2026-10-13 19:40:32 +00:00
aiongg 66146fd866 Integration: #754 IDML keeps Basic Feather choke and corners (FeatherSetting ChokeAmount, CornerType); zh Sharp is 锐利 2026-10-13 19:35:14 +00:00
aiongg f60eb24656 Merge #823: Gradient strokes: on screen, in PDF, in IDML and the Gradient panel
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/object.rs
#	crates/engine/src/cmd/style.rs
#	crates/idml/src/tests.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/uk.rs
#	crates/ui-egui/src/panels/swatches.rs
#	docs/target-app-parity.md
2026-10-13 19:28:11 +00:00
Govinda RoyandClaude Opus 5.5 82ffb692fc INDD: graphics whose linked file is missing show InDesign's preview
An InDesign document holds a screen preview of each placed graphic, which
InDesign shows while the linked file is missing. indd-utils returns them
beside the IDML (`Conversion::previews`); opening an .indd now passes them
to the importer through `import_idml_report_with_previews`, and a graphic
whose file neither the package nor the folder provides takes its preview.
The graphic keeps its link, so relinking replaces it; IDML import without
previews is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:03:19 +06:00
moppeeerandClaude Opus 5.5 370dca2ca2 feat(render): gradient strokes, on screen, in PDF and in the Gradient panel
A gradient swatch on a stroke drew nothing: strokes were painted with a
single resolved colour. LYH Studio episode 6 gives the loop on the right
page (9:13) and, at first by accident, the circle (1:19, "right now it
is on the stroke") a gradient stroke.

- doc: Stroke::gradient_angle / gradient_vector (as Fill's) and
  Stroke::as_fill(), the stroke's paint as a fill.
- render and PDF: a gradient stroke is painted like a gradient fill,
  across the path's box (dash gaps and arrowheads keep their paints).
- engine: object.gradient {target: stroke} edits the stroke's
  gradient (kind, stops, angle, the Gradient Swatch tool's from/to);
  scaling moves the stroke's vector with the shape.
- IDML: GradientStrokeStart/Length/Angle round-trip.
- Gradient panel: acts on the stroke when the toolbox's Stroke box is
  in front, as InDesign's follows the Fill/Stroke proxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:11:58 +02:00
aiongg a4f4a95901 Merge idml-overrides: fix(idml): export keeps overridden parent items and their local copies' links (#725) 2026-10-13 14:31:45 +00:00
aiongg 06762c3bfc fix(idml): export keeps overridden parent items and their local copies' links (#725) 2026-10-13 14:17:13 +00:00
aiongg 5f2110e7e5 Merge pr/687: Bump quick-xml to 0.41 (#687) 2026-10-13 12:13:14 +00:00
natocool2andClaude Opus 5.5 5a5e97b0a6 Bump quick-xml to 0.41 (RUSTSEC-2026-0194, RUSTSEC-2026-0195)
The .docx/.xlsx text import iterates attributes with quick-xml's default duplicate check, so one crafted
start tag with N attributes cost O(N^2) (RUSTSEC-2026-0194, fixed in quick-xml 0.41). Bumping also clears
RUSTSEC-2026-0195 (NsReader namespace allocation), which this workspace does not reach (no NsReader).

unescape_value() is deprecated in 0.41 (clippy -D warnings); normalized_value(Implicit1_0) is what the
deprecated method now does. Regression test: an element with 100,000 attributes parses in < 5 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 09:07:47 +01:00
Brandon Thomas a390905beb Merge branch 'pr/647' into batch1
# Conflicts:
#	crates/tools/src/lib.rs
#	crates/tools/src/tests.rs
#	crates/ui-egui/src/canvas.rs
2026-10-13 02:23:33 -04:00
aiongg f9bc8a17a2 Merge #671: PARITY-IDML-INDEX: retain index reference source sort keys 2026-10-13 01:15:31 +00:00
aiongg 8a6330b170 Integration (#653 x #643): the object style round-trip test expects the default drop shadow angle (120°) 2026-10-13 01:15:06 +00:00
aiongg 2e1002cb1b Integration (#653 x #418): one ObjectStyle BasedOn walk; style effects read the chain #418's object_style_chain walks 2026-10-13 01:06:15 +00:00
aiongg b2aa5c1ec8 Merge object-style-effects: Object styles carry effects (#653) 2026-10-13 01:05:37 +00:00
aiongg ebae0c7805 Merge parent-based-on: Parents: a parent can be based on another parent (#652)
# Conflicts:
#	ROADMAP.md
#	crates/compose/src/cache.rs
#	crates/doc/src/build.rs
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/layout.rs
#	crates/idml/src/import.rs
#	crates/idml/src/tests.rs
#	crates/pdf/src/tests.rs
#	crates/render/src/lib.rs
#	crates/render/src/text.rs
#	crates/tools/src/snap.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 01:00:08 +00:00
aiongg 462831bf81 fix(styles): object style effects keep local effects, validate values, follow BasedOn in IDML (#653) 2026-10-13 00:58:47 +00:00
sanyamsingh001andCodex 8f48869e58 PARITY-IDML-INDEX: retain index reference source sort keys
Co-Authored-By: Codex <codex@openai.com>
2026-10-13 10:56:09 +10:00
aiongg 182db943c2 fix(parents): bound every based-on walk; base items follow the parent showing them (#652)
Cycle checks walk based_on with a seen-set over all parents, so a cycle
longer than the drawing depth cap is still found. Snapping lists parent
items through shown_parent_pages instead of recursing, and offers base
items on a parent spread. A base parent's text on a parent spread is
composed for that spread, so page-number markers show its prefix.
Cmd+Shift-click hits what is drawn: it honours Show Parent Items, hidden
and locked layers, the facing-page filter and layer order. IDML import
warns when a parent spread's pages name different bases or their own
spread.
2026-10-13 00:51:41 +00:00
aiongg 98ff430022 feat(styles): object styles carry effects (#653) 2026-10-13 00:40:59 +00:00
aiongg 80803e3ae3 feat(parents): a parent can be based on another parent (#652)
Pages draw the base parent's items, then the parent's, then their own,
on the canvas, in PDF and image export, and on the parent spread itself.
Chains are followed base first with a depth bound. Overrides, parent hit
testing and the flattener's transparency check include base items.

layout.parents.options {parent, basedOn} sets or clears the base and
refuses cycles; document.inspect reports basedOn per parent. Opening a
.designcraft or IDML file clears a cyclic or dangling base with a warning.
IDML already carried the base as the parent pages' AppliedMaster; a test
covers the round trip.
2026-10-13 00:21:48 +00:00
aiongg 99e6922981 Merge pr/636: Tools: drag type-on-a-path start and end brackets (#636)
# Conflicts:
#	ROADMAP.md
#	crates/idml/src/tests.rs
#	crates/tools/src/lib.rs
#	crates/tools/src/select.rs
#	crates/tools/src/tests.rs
#	crates/ui-egui/src/canvas.rs
2026-10-13 00:08:12 +00:00
aiongg d7d71380ad Merge pr/610: Links: placed text files can stay linked (Word, RTF, text) (#610)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/linked.rs
#	crates/engine/src/cmd/links.rs
#	crates/engine/src/cmd/place_text.rs
#	crates/engine/src/cmd/prefs.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-13 00:00:51 +00:00
aiongg e391002be6 Merge text-stroke-behind: Text: stroke behind fill (#619)
Conflict: crates/render/src/text.rs: the branch's paint_glyphs with next's fill and stroke overprint (overprint_fill / overprint_stroke, an overprinting() Multiply layer around each paint); next's cell_stroke(kind, weight, length); path text strokes don't overprint, as before
Conflict: crates/pdf/src/text.rs: the branch's text_run / paint_outlines / glyph_outlines split with next's skew tan, vertical mark offsets and split_clusters glyph order (#421)
Conflict: crates/engine/src/cmd/paths.rs: next's shatai transform + the branch's stroke_behind_fill key
Conflict: crates/idml/src/tests.rs: tests both
Conflict: docs/typography-parity.md, ROADMAP.md: headers combined, logs both
2026-10-12 22:04:10 +00:00
aiongg 270dd46b50 Text: stroke behind fill (#619)
A character attribute strokeBehindFill paints a text stroke under the fill
at double width, so its full weight lies outside the letters and they keep
their shape. Screen and PNG rendering, PDF export (stroke as outlines
first, then the filled text, so the text is not duplicated; clipped to the
outside of the glyphs when there is no fill), Create Outlines (outside-
aligned stroke), the Properties panel and Character Style Options.

IDML StrokeAlignment on CharacterStyleRange, CharacterStyle and
ParagraphStyle imports and exports as the attribute (OutsideAlignment =
on, CenterAlignment = off).
2026-10-12 21:06:42 +00:00
Isaac Levine d66f2c47ae Engine: route book chapters and atomic saves through the checked writers, and take file access out of fonts, IDML and ICC loading
Rebased onto the atomic save work: book chapter replacement, canonicalize and
the writability probe, and every command write now check the roots before
they reach crate::atomic. IDML import no longer reads linked images itself
(the engine passes its checked reader), load_icc_file is gone (profiles
arrive as bytes), the script loader goes through the host reader, and
FontDb::load_document_fonts asks a caller-supplied check about the folder and
each font file, so a link that leads out of the read root is skipped.
2026-10-12 16:50:04 -04:00
moppeeerandClaude Opus 5.5 cded7d997e feat(tools): Drag type-on-a-path start and end brackets
Selected type on a path shows its start and end brackets across the path,
as in InDesign, and the Selection and Direct Selection tools drag them
along it (Move Bracket, one undo step): the text starts and ends where
they are, and text past the end bracket is overset.

- doc: PathType.end (None: the path's end)
- geom: PathWarp::nearest, the arc length of the nearest point
- engine: type.pathOptions {end}; brackets stay on the path and can't cross
- compose: the line runs from the start to the end bracket
- idml: EndBracket read and written
- tools: Cursor::PathBracket

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:32:56 +02:00
aiongg e2dd71378e Merge #609: Balance Columns evens out the last frame's columns
balance-columns 30ba4721.

Conflict: crates/compose/src/lib.rs: story-end check (balance_end) before next's deleted-paragraph filler, then prange; next's `go` closure with the branch's band tail on Next Column; next's Cursor::clip replaced by the branch's tail-aware clip (the auto-placed duplicate dropped)
Conflict: crates/compose/src/tests.rs, crates/idml/src/tests.rs: tests both
Conflict: ROADMAP.md, docs/target-app-parity.md: both (auto-size and Balance Columns lines)
2026-10-12 20:06:06 +00:00
aiongg 38bdc19232 Merge #180: text frame auto-size grows and shrinks frames to fit their text
text-autosize 60551322.

Conflict: crates/doc/src/item.rs: next's reference-point doc and default auto_size_ref 4 (centre, as IDML leaves it) plus the branch's minimum-size and No Line Breaks fields
Conflict: crates/engine/src/lib.rs: autosize pass before next's record_undo_step on the success path; bump_revision for next's global revisions
Conflict: crates/engine/src/cmd/interchange.rs: next's grep_style_warnings and .indd comment plus the branch's autosize::apply on import
Conflict: crates/engine/src/cmd/object.rs: branch's textFrameOptions params doc with next's firstBaseline values
Conflict: ROADMAP.md: progress log both
Conflict: docs/gaps.md: next's optical kerning wording with the branch's auto-size note on #180
2026-10-12 19:57:02 +00:00
aiongg 30ba47211e Text frames: Balance Columns evens out the last frame's columns (#609)
In the frame a story ends in, Balance Columns lowers the bottom of the
columns after the last explicit column break as far as the text still
fits, reusing the bounded search that balances columns above spanning
paragraphs. Frames the text runs on from fill normally. Text Frame
Options gets a Balance Columns checkbox.
2026-10-12 19:18:45 +00:00
aiongg 605513228a Text frames: auto-size grows and shrinks frames to fit their text
Height Only, Width Only, Height and Width and Keep Proportions frames fit
their text after every undoable command (same undo step) and after IDML
import, around the reference point, with minimum height/width and No Line
Breaks. Text Frame Options gains an Auto-Size section; IDML reads and writes
the minimum and no-line-break attributes. Applies to unthreaded frames and
the last frame of a thread; vertical frames and type on a path are left as
they are. (#180)
2026-10-12 18:55:57 +00:00
aiongg 7cc6f6db72 Merge idml-deleted-text (update): deleted text inside nested paragraphs stays deleted; a deleted break doesn't break (#581)
Conflict: crates/idml/src/tests.rs: both sides' tests (#473's SameParaStyleSpacing tests, then the branch's deleted-text tests)
2026-10-12 17:25:31 +00:00
aiongg bfaebb76ee Integration: an explicit SameParaStyleSpacing Ignore survives a native save (#473)
same_para_style_spacing uses deserialize_explicit_value, so Some(None) round-trips through
serde. Test: SameParaStyleSpacing as a Properties element on the based-on Normal style closes
the gap between consecutive headings; SetIgnore keeps the usual spacing.
2026-10-12 17:18:26 +00:00
aiongg dad91f0c74 Merge #473: Honor IDML SameParaStyleSpacing between same-style paragraphs (#449)
Conflict: crates/compose/src/lib.rs: same-style gap replaces the pending space; otherwise next's text_above span space-before rule
Conflict: crates/doc/src/attrs.rs: same_para_style_spacing before the drop-cap fields and their doc
Conflict: crates/compose/src/tests.rs: both sides' tests
Conflict: crates/idml/src/tests.rs: both sides' tests
2026-10-12 17:16:10 +00:00