Commit Graph
4771 Commits
Author SHA1 Message Date
kuseidos bb54c44ad9 Command API: refuse a page index past the end, and say how many pages went (#2460)
* Command API: refuse a page index past the end, and say how many pages went

layout.pages.delete answered ok for an index past the last page and deleted nothing for it, so a caller off by one removed a different range and read a success; on a 336-page book that is page 337 instead of 336. Both commands now check every index against the document and refuse it with the sentence the MCP export commands have used since #70, return how many pages they touched so a no-op and a success are told apart, and state in their params that the indices count from 0.

* Pages: count the pages a delete will remove, not the indices it was given

delete_pages refused [0, 0, 1, 2] on a four-page document as if it would leave none, because the guard measured the length of the list instead of the pages it names. Same family as the index check next to it: the count was of the wrong thing. A test in the engine pins it.

* Pages: refuse a malformed page list instead of dropping what it cannot read

Review finding: page_indices used filter_map, so a list holding a string, a fraction or nothing at all was silently shortened — the caller was told a page was handled when it never was, which is the same mistake the command now refuses one level down. Every entry must be a whole number, and pages must be a list. Tests cover all three shapes, and the new test functions carry docstrings so the coverage check has nothing to report.
2026-11-09 04:05:22 +00:00
Dylan M. Taylor f7d3b3b376 Merge pull request #2506 from dotCooCoo/chore/craft-update-1b34eba41
Updates: take craft-update from craft-libs main at 1b34eba41
2026-11-08 22:55:43 -05:00
dotCooCoo 79953b8ae3 Updates: take craft-update from craft-libs main at 1b34eba41 2026-11-08 19:45:45 -08:00
Dylan M. Taylor b08d60b912 Merge pull request #2488 from dotCooCoo/chore/craft-update-updater-fixes
Updates: the updater reports installing, ready, installed and failed, and installs at exit
2026-11-08 22:12:35 -05:00
autocraft-bot[bot] ded1e9f571 Merge pull request #2505 from Silvertree2010/fix/h14080-rtf-import-puts-the-text-of-head
Skip RTF header/footer left, right and first-page groups on import
2026-11-09 02:16:55 +00:00
NetterSilverandClaude Haiku 5.5 55729a5128 Skip RTF header/footer left, right and first-page groups on import
Found while testing crates/textimport/src/rtf.rs:43-63 (SKIP list) and :201 (`SKIP.contains(&word.as_str())`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 02:45:42 +01:00
Dylan M. Taylor a337204cfe Geom: step budget in Pencil fit, stripe band cap, balanced Pathfinder union (#2251)
* geom: step budget in freehand fit, stripe band cap, balanced unions

* move test
2026-11-08 22:56:03 +00:00
aiongg 49f6b85cb9 Merge pull request #2037 from r31lin/fix/gpu-largest-buffer
Ask the graphics device for the adapter's largest buffer
2026-11-08 16:10:35 -05:00
dotCooCoo 076c6b1afc Updates: the updater reports installing, ready, installed and failed, and installs at exit
craft-update moves from c5a1cbfb to c7c84230 on dotCooCoo/craft-libs, a
branch that merges storytold/craft-libs#687 and #696 into craft-libs main.

The check result window shows for every status but idle and stays closed
while the startup prompt is open. At the first start after an update it
reports that the update installed or why it failed. Its Restart to Update
calls Updater::restart_to_update. The install helper now starts when the
app exits and drops the updater.
2026-11-08 12:46:13 -08:00
aiongg b0c3f85cc8 Merge pull request #2480 from Silvertree2010/fix/h12511-xlsx-cell-reference-parser-accep
Reject signed row numbers in XLSX cell references
2026-11-08 12:28:26 -05:00
NetterSilverandClaude Haiku 5.5 5da2393e28 Reject signed row numbers in XLSX cell references
Found while testing crates/textimport/src/xlsx.rs:15

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 18:14:57 +01:00
aiongg 2e9b46c1d5 Merge pull request #2479 from Silvertree2010/fix/h12181-file-new-and-file-savepreset-acc
Clamp negative gutter in file.new and file.savePreset to 0
2026-11-08 09:43:29 -05:00
aiongg 8e34117922 Apply review fixes
- crates/engine/src/cmd/file/new.rs
2026-11-08 14:29:02 +00:00
NetterSilverandClaude Haiku 5.5 4a7fc340f8 Clamp negative gutter in file.new and file.savePreset to 0
Found while testing crates/engine/src/cmd/file/new.rs:179 (apply_settings: `nd.gutter = f64_or(p, "gutter", nd.gutter)`); compare crates/engine/src/cmd/layout/margins.rs:101 (`g.max(0.0)`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 15:10:20 +01:00
aiongg 9cf4e3a380 Merge pull request #2477 from Silvertree2010/fix/h11767-gradient-expanded-stops-does-not
Clamp midpoint in Gradient::expanded_stops like sample does
2026-11-08 08:16:36 -05:00
aiongg 8ca6149325 Apply review fixes
- crates/pdf/src/gradient_inks.rs
2026-11-08 13:06:21 +00:00
NetterSilverandClaude Haiku 5.5 c73699374f Clamp midpoint in Gradient::expanded_stops like sample does
Found while testing crates/color/src/gradient.rs:~101-110 (expanded_stops); the unclamped value comes from crates/idml/src/import/swatches.rs:128-129

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 12:49:47 +00:00
aiongg 19c9875770 Merge pull request #2470 from Silvertree2010/fix/h11260-docx-xlsx-xml-parser-accepts-sig
Drop signed numeric character references in DOCX XML parser
2026-11-08 03:47:30 -05:00
NetterSilverandClaude Haiku 5.5 8e5e40d76b Drop signed numeric character references in DOCX XML parser
Found while testing crates/textimport/src/docx.rs:132-133 (parse, Event::GeneralRef branch)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 08:32:16 +00:00
aiongg d848948b11 Merge pull request #2464 from Silvertree2010/fix/h10935-cross-reference-format-with-an-u
Apply typographic quotes when xref format has an unterminated '<'
2026-11-08 00:24:05 -05:00
NetterSilverandClaude Haiku 5.5 88a45f5d58 Apply typographic quotes when xref format has an unterminated '<'
Found while testing crates/doc/src/xref.rs:~100 (expand, early `return out` in the `tag_end` None branch)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 06:10:10 +01:00
Kushagra Kumar f9cfd99adc Text: adjust hyperlink and placeholder source ranges on story edits (#1219) (#1229)
* Text: adjust hyperlink and placeholder source ranges on story edits (#1219)

Text-anchored hyperlinks and data-merge placeholders store byte ranges
(`start..end`) pointing into story text. When text earlier in the story is
inserted or deleted, these offsets remained unchanged, causing hyperlink
targets and data-merge placeholders to drift across unrelated text or
point to invalid UTF-8 boundaries.

This change:
- Implements `Document::adjust_text_ranges` in `designcraft-doc` to shift
  or resize text hyperlinks and placeholders when story text is edited.
  Pure insertions at or before the link shift it forward; insertions
  inside expand the link's span; deletions before shift it back; deletions
  consuming or overlapping the link clamp to surviving text or prune it if
  entirely consumed.
- Cleans up obsolete hyperlinks and placeholders in `set_story_text`.
- Wires range adjustment into engine text commands: typing, deleting,
  selection replacement, autocorrect, `story.replaceRange`, find/change,
  and spelling change.
- Adds comprehensive unit tests in `designcraft-doc` and 7 integration
  regression tests in `crates/engine/tests/hyperlink_offsets.rs`.

* fix(engine): adjust hyperlink and placeholder ranges for physically removed text under track changes

* fix(engine): adjust text ranges for formatted paste, case change, and drag move

* fix(engine): record change case adjustments per character to preserve enclosed hyperlinks

* fix(engine): adjust text ranges for track changes, inline markers, and preserve zero-length ranges

- Route Track Changes accept/reject deletions through adjust_text_ranges, and transfer replaced text hyperlinks to inserted text so links survive accept.
- Hook inline marker operations to adjust_text_ranges (notes, endnotes, xref, anchored objects, index markers, tables, editorial notes, unvar, and place text).
- Replace datamerge::shift_after with adjust_text_ranges.
- Only prune ranges that the edit emptied (s < e && s_new >= e_new), preserving zero-length ranges.
- Drop inverted ranges first and use saturating arithmetic in remap_range.
- Preserve doc.hyperlinks and placeholders ordering in move_text_ranges by mutating in place.
- Remove dead wrapper mark_deleted and call mark_deleted_reporting.
- Fix changes.track invocation parameter to 'on'.
- Add comprehensive regression tests in doc and engine covering all review items.

* fix(text): guard cell find/change, pre-record tracked replaces, shift para xref anchors

* fix(text): record stable IDs for tracked replacement redirects

* fix(text): reconcile range adjustments for review feedback

* fix(text): adjust ranges on delete surrounding and clean up dead auto edit

* Prune text ranges on TOC index and live caption regeneration

* test(engine): format regenerating_index_prunes_story_hyperlinks line wrap
2026-11-07 19:54:00 -06:00
Luis 1da6effdeb Release: refresh contributor credits for each release (#2424) (#2426)
About > Contributors still showed an old snapshot, because nothing
regenerated contributors/contributors.json before a release. Opt in to
the shared refresh from craft-common-actions: a read-only credits job
regenerates the snapshot for the release commit, and the shared release
workflow installs it before every source build. The snapshot is not
committed back.
2026-11-08 01:32:13 +01:00
FredApps 7e32b5ffd5 Merge pull request #2412 from ZIDAN44/fix/2401-white-overprint-preflight
Preflight: white or [Paper] set to overprint is reported (#2401)
2026-11-07 15:38:00 +01:00
aiongg 894968eeee Merge pull request #2442 from Silvertree2010/fix/h8179-lab-hue-can-return-360-although
Keep Lab::hue within 0..360 for tiny negative b
2026-11-07 08:12:01 -05:00
NetterSilverandClaude Haiku 5.5 6365f98abe Keep Lab::hue within 0..360 for tiny negative b
Found while testing crates/color/src/cms/lab.rs:25-27 (Lab::hue)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 12:56:41 +00:00
aiongg e692677a18 Merge pull request #2437 from Silvertree2010/fix/h8141-hit-intersects-rect-returns-fals
Normalize marquee rect in hit::intersects_rect
2026-11-07 05:27:43 -05:00
NetterSilverandClaude Haiku 5.5 2fb2689efe Normalize marquee rect in hit::intersects_rect
Found while testing crates/geom/src/hit.rs:28-35 (intersects_rect: `path.anchors().any(|(_, _, a)| r.contains(a.p))` and the early-out using `b.intersect(r)`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 11:14:34 +01:00
aiongg e977f82558 Fix [Bug Report] [DesignCraft] Image content and frame don't move together (#2423)
Fixes #2422.

- crates/engine/src/cmd/object.rs
- crates/engine/tests/autocraft_regression_2422.rs
2026-11-06 18:21:30 -05:00
Zinadin Zidan cd315908aa Preflight: a frame with states is looked at as PDF export draws it (review)
Flex Layout's convert turns a multi-state group into a frame that keeps
its states. PDF export draws every object pasted into a frame, but the
white overprint check walked only the active state, so a white
overprint in another state printed unreported. Which children export
draws is now one function, drawn_children, used by both.
2026-11-07 03:07:28 +06:00
Zinadin Zidan ecf7e23085 Merge the review fix pushed to fix/2401-white-overprint-preflight
83d11697 reported white ruby and kenten on the frame that shows the run's
first character. The review commit before this merge does the same through
white_overprint::frames_showing, and also reports a run on every frame it
spans, leaves overset text out, and reads the composition cache only when a
story has white ruby or kenten. preflight.rs keeps that version; the tree
is unchanged by this merge.
2026-11-07 02:11:59 +06:00
Zinadin Zidan 231ee6d4c3 Preflight: white overprint is looked for where PDF export prints (review)
Ruby and kenten in a threaded story were reported against the story's
first frame, on its page, wherever the text was; they are now reported
against the frame that shows them, and text no frame has room for isn't.
Parent pages' items and anchored objects print too and weren't looked
at; items on a hidden or nonprintable layer, or in a hidden or
nonprinting group, don't print and were reported. The check now walks
the items as PDF export draws them, with its rules (the parent page
items a page shows now come from one function both use), and a frame
PDF export leaves out reports none of its ruby and kenten.
2026-11-07 02:09:42 +06:00
aiongg 83d11697db Apply review fixes
- crates/engine/src/cmd/preflight.rs
2026-11-06 19:37:05 +00:00
aiongg c99a41af44 Fix iDML import drops a table cell's insets written as TextTopInset … (#2414)
Fixes #2410.

- crates/idml/src/import/table.rs
- crates/idml/src/import/styles.rs
- crates/idml/src/export/table.rs
- crates/idml/src/export/styles.rs
- crates/idml/tests/autocraft_regression_2410.rs
2026-11-06 14:28:53 -05:00
Zinadin Zidan 085a120606 Preflight: white or [Paper] set to overprint is reported (#2401)
A fill, stroke or stroke gap set to overprint in [Paper] or in a process
colour that lays down no ink (C=0 M=0 Y=0 K=0, gray 0, RGB white, a 0%
tint) disappears on press. Preflight now reports it as whiteOverprint, and
ruby and kenten set to overprint in such a colour too. A spot ink (a white
ink or a varnish) prints whatever colour stands in for it, so it isn't
reported. Text has no overprint of its own, so white text knocks out and
prints, and isn't reported. The old Preflight note that PDF export drops
ruby and kenten overprint is gone: export writes it.
2026-11-07 00:27:10 +06:00
FredApps 3e044b0690 Merge pull request #2390 from storytold/house-rules
Copy the shared house rules into AGENTS.md
2026-11-06 14:59:55 +01:00
FredApps d1ed395688 docs: preserve local guidance in house-rules rollout 2026-11-06 12:36:55 +01:00
FredApps 542b5654b3 Copy the shared house rules into AGENTS.md.
The blocks come from craft-contrib at dde09ea31e2e457f5b4653a6e76f7cdf55944bdf. CLAUDE.md imports AGENTS.md. The house-rules workflow checks the copy.
2026-11-06 11:50:01 +01:00
Hamza Naveedandskylar taylor-barrick 8fce28258d IDML export: a document's hyperlinks are written out (#1446) (#1659)
* IDML import: a page item's or a text run's hyperlink survives (#1446)

IDML carries a hyperlink as three elements: a source, a destination and a
`Hyperlink` joining them by `Self`. Only `CrossReferenceSource` was mapped, so
an image frame linked to a photo and a run of text linked to a page were
dropped on import, although both are native here and both export to PDF.

The names are collected as the package is read and the two ends put together
in `finish`, once the pages, page items and anchors they name exist. A link
whose source or destination is not in the file is dropped with a warning
saying which, rather than silently or as a link that goes nowhere.

Carries all three review fixes:

- a source recorded in a table cell's or a footnote's story is dropped. Those
  stories are built under the StoryId(0) placeholder, so the link named a
  story nothing can look up, with offsets into text that is not there. The
  warning says where the text was instead of claiming it was not text.
- the same for a HyperlinkTextDestination in such a story: its anchor id is
  (0 << 24) | k, so it collides with other cells' anchors and belongs to no
  story the cross-reference index holds; the link resolved to Anchor(1) and
  the PDF left the annotation out without a word.
- the index's topic cross-references go in at the start of the first story
  with text after the ranges are recorded, so the ranges for that story are
  shifted with the markers; a range left alone covered the wrong nine bytes.

Rebased onto the post-split main, hunk by hunk: the Importer struct, its
fields, the element scan, hyperlink_list and finish are in import.rs, while
StoryBuilder, walk_story and the anchor list are in import/story.rs. finish
answers (Document, Vec<String>) so the warnings reach the caller.

* An anchor a hyperlink cannot mean is refused, not ignored

This branch carries its own copy of `dest_param`'s anchor arm -- the IDML import
needed anchors as link destinations and added it here independently of #1555 --
and it has the same hole, so review is right that the fix has to be here as well
rather than only on that PR.

`dest_param` returns `Result<Option<_>>`. `None` still means no destination was
given, which `hyperlink.edit` needs for an edit that only renames; an `anchor`
that is given and names none is an error.

An unknown *id* was accepted outright, since `as_u64` answers for any number, so
a link was stored to an anchor nobody has and the export quietly left it out. On
`hyperlink.edit` the `None` read as "no destination given", so the command
reported success and kept the old destination, which is the case review
reported. An unknown *name* did already fail on `hyperlink.create`, but with
"give `url`, `email`, `page` or `anchor`" -- the wrong problem named.

The same change is on #1555, deliberately: each PR introduces this code on its
own line and each has to be right on its own. Whichever lands first, the other's
conflict is the same text on both sides.

* IDML export: a document's hyperlinks are written out (#1446)

Import reads hyperlinks now, but export dropped them, so a round trip lost
every link the document had. The three elements IDML wants are written: a
destination, a source, and a `Hyperlink` joining them.

A link on a run of text is a `HyperlinkTextSource` around the ranges it
covers, and the element nests inside a `ParagraphStyleRange`, so a link whose
text crosses a paragraph is written as one source per paragraph and each piece
stays clickable. Consecutive ranges of one link share a single source element.

Carries both review fixes:

- a link to an anchor whose text has been deleted is not written. The anchor
  is resolved first, as the page destination's page already was, so the file
  cannot hold a `Hyperlink` whose `Destination` names no element.
- every `Self` names one element. Three links on one frame wrote three
  `HyperlinkPageItemSource` elements all called i12, and each link to a page
  pushed another `HyperlinkPageDestination`; the item source carries the
  link's index now, and the page destination is written once per page as
  url_dest_el already did for URLs. The round-trip test collects every `Self`
  in the designmap and asserts none repeats, so an element added later is
  covered without anyone remembering to extend it.

Rebased onto the post-split main, on top of the rebased #1652: the `Ex` state
is in export.rs, the hyperlink elements are written in export/designmap.rs
with url_dest_el, fnv and has_self beside their only caller, and the run
cutting and wrapping is in export/story.rs.

* Split the hyperlink and index-marker steps out of Importer::run, under clippy's complexity limit

* Split the hyperlink and index-marker steps out of Importer::run, under clippy's complexity limit

* Split the hyperlink steps out of story_para, under clippy's complexity limit

* IDML import: say when a link's frame is anchored in text, which PDF export cannot place yet

---------

Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
2026-11-06 10:01:02 +01:00
Hamza Naveedandskylar taylor-barrick 6a85f42263 IDML import: a page item's or a text run's hyperlink survives (#1446) (#1652)
* IDML import: a page item's or a text run's hyperlink survives (#1446)

IDML carries a hyperlink as three elements: a source, a destination and a
`Hyperlink` joining them by `Self`. Only `CrossReferenceSource` was mapped, so
an image frame linked to a photo and a run of text linked to a page were
dropped on import, although both are native here and both export to PDF.

The names are collected as the package is read and the two ends put together
in `finish`, once the pages, page items and anchors they name exist. A link
whose source or destination is not in the file is dropped with a warning
saying which, rather than silently or as a link that goes nowhere.

Carries all three review fixes:

- a source recorded in a table cell's or a footnote's story is dropped. Those
  stories are built under the StoryId(0) placeholder, so the link named a
  story nothing can look up, with offsets into text that is not there. The
  warning says where the text was instead of claiming it was not text.
- the same for a HyperlinkTextDestination in such a story: its anchor id is
  (0 << 24) | k, so it collides with other cells' anchors and belongs to no
  story the cross-reference index holds; the link resolved to Anchor(1) and
  the PDF left the annotation out without a word.
- the index's topic cross-references go in at the start of the first story
  with text after the ranges are recorded, so the ranges for that story are
  shifted with the markers; a range left alone covered the wrong nine bytes.

Rebased onto the post-split main, hunk by hunk: the Importer struct, its
fields, the element scan, hyperlink_list and finish are in import.rs, while
StoryBuilder, walk_story and the anchor list are in import/story.rs. finish
answers (Document, Vec<String>) so the warnings reach the caller.

* An anchor a hyperlink cannot mean is refused, not ignored

This branch carries its own copy of `dest_param`'s anchor arm -- the IDML import
needed anchors as link destinations and added it here independently of #1555 --
and it has the same hole, so review is right that the fix has to be here as well
rather than only on that PR.

`dest_param` returns `Result<Option<_>>`. `None` still means no destination was
given, which `hyperlink.edit` needs for an edit that only renames; an `anchor`
that is given and names none is an error.

An unknown *id* was accepted outright, since `as_u64` answers for any number, so
a link was stored to an anchor nobody has and the export quietly left it out. On
`hyperlink.edit` the `None` read as "no destination given", so the command
reported success and kept the old destination, which is the case review
reported. An unknown *name* did already fail on `hyperlink.create`, but with
"give `url`, `email`, `page` or `anchor`" -- the wrong problem named.

The same change is on #1555, deliberately: each PR introduces this code on its
own line and each has to be right on its own. Whichever lands first, the other's
conflict is the same text on both sides.

* Split the hyperlink and index-marker steps out of Importer::run, under clippy's complexity limit

* IDML import: say when a link's frame is anchored in text, which PDF export cannot place yet

---------

Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
2026-11-06 09:41:11 +01:00
FredApps 33b6768dd7 Merge pull request #2368 from ZIDAN44/fix/2345-paste-into-frame-edges
Frame edges: a frame holding pasted-in content draws its own edge, not its content's (#2345)
2026-11-06 00:41:06 +01:00
aiongg 601a912520 Apply review fixes
- crates/ui-egui/src/canvas/frames.rs
2026-11-05 22:02:46 +00:00
Hamza Naveed 4b63d26ac6 A hyperlink can point at a text anchor (#1446) (#1555)
* A hyperlink can point at a text anchor (#1446)

A frame or a text range could link to a URL, an email address or a page
number. A page number is the wrong thing to store for "this thumbnail links to
that photo": the photo moves and the link does not. InDesign stores the
destination, and IDML files do too — a `HyperlinkPageItemSource` pointing at a
`HyperlinkTextDestination` is how a report links its thumbnails to the
full-size pictures (#1446).

`HyperlinkDest::Anchor(id)` is a text anchor, the same destination a
cross-reference targets, and the PDF export resolves it the same way: through
the cross-reference index, to whichever page the anchor's text has landed on.
A link to an anchor that no longer exists is left out of the file rather than
pointing somewhere wrong.

`hyperlink.create` and `hyperlink.edit` take `anchor` — an anchor's id or its
name — and a link that is given no name of its own takes the anchor's.

Measured: a thumbnail on page 1 linked to an anchor in text on page 3 exports
`/Dest [<page 3> 0 R /XYZ 0 300 0]`; insert a page in front and the same link
exports page 4, with no edit to the link.

This is the groundwork for the IDML half of #1446 (import and export of
page-item and text hyperlinks), which is the next change and needs a
destination to put them in.

* An anchor a hyperlink cannot mean is refused, not ignored

This PR added anchors as link destinations and did not check that the anchor
exists, so the feature was wrong on its own and review was right not to take the
fix from a PR stacked above it.

`dest_param` returns `Result<Option<_>>` now. `None` still means no destination
was given, which `hyperlink.edit` needs for an edit that only renames; an
`anchor` that is given and names none is an error.

Two behaviours follow. An unknown *id* was accepted outright -- `as_u64`
answered for any number -- so a link was stored to an anchor nobody has and the
export quietly left it out. And on `hyperlink.edit` the `None` read as "no
destination given", so the command reported success and kept the old
destination, which is the case review reported.

An unknown *name* did already fail on `hyperlink.create`, but with "give `url`,
`email`, `page` or `anchor`", which named the wrong problem; it says "no text
anchor X (anchor.list names them)" now.

The test covers the id, the name, both on `edit`, that a refused edit leaves the
destination, the name and the exported page alone, and that a rename naming no
destination still works -- that last one is what `None` is for and it must not
have become an error on the way.

The page-number validation #1564 adds stays there; this is the anchor half,
which belongs with the anchor feature.

* A name two anchors share cannot pick one of them

`anchor.create` does not require an anchor's name to be unique, and an IDML
import can bring a document that already has two of the same. The lookup took
the first match, so a link made on a shared name went to whichever anchor came
first -- a different page, with nothing to say so (noted in review).

An id still names one anchor or none, and is checked as before. A name that two
anchors answer to is refused, with their ids in the message so the caller can
say which one they meant.

Not uniqueness at creation, which was the other way offered: a document can
already hold duplicates, so enforcing it there would refuse to open files that
are fine today, and the ambiguity only matters where a name is being resolved.

The test builds two anchors of one name on different pages, asserts both
`hyperlink.create` and `hyperlink.edit` refuse it and say what to do instead,
that the refused create made no link and the refused edit left the destination
alone, and that each anchor is still reachable by its id. It is a test of its
own because the twin anchor outlives the deletion the other test ends with.

* Test: two anchors sharing a name come from a file, now that anchor.create refuses a name in use

main's anchor.create now refuses a second anchor of a name, so the test makes the first and renames the second in the document, as a file opened with two can hold them. The comment on the guard says the same.
2026-11-05 22:24:10 +01:00
Hamza Naveed 59501a26f9 An item a parent overrode and deleted is gone from the pages that use it (#1469) (#1558)
* An item a parent overrode and deleted is gone from the pages that use it (#1469)

In InDesign, removing an inherited item from a parent page means overriding
it there and deleting the override: the parent's page keeps the item in its
override list with no copy of its own. Only the page being drawn was checked,
so the pages that use that parent still drew the base's item -- a signature
block from parent B over the notice text of every page using parent E.

The override lists of the whole parent chain are checked, not just the page's
own. An override always names an item of a parent further back, so one set
covers the chain.

Rebased onto the post-split main: the parent-item loop moved from render's
lib.rs to draw.rs and the tests to tests.rs.

One thing this does not touch: blendspace.rs checks `page.overridden` the same
way in two places, for deciding which spreads hold transparency. A deleted
override there would count an absent item as present, which can only cost an
unnecessary flatten rather than draw the wrong thing, so it is left for a
maintainer to judge rather than widened into this fix.

* Parent items: one set of overridden items for drawing, PDF and PPTX export, hit-testing, snapping and frame edges

* Override All: an item a parent removed is not brought back as a copy
2026-11-05 22:22:47 +01:00
Zinadin ZidanandClaude Opus 5.5 f5581e3c33 Frame edges: a frame holding pasted-in content draws its own edge, not its content's (#2345)
draw_item_edges went into every Content::Group, including a frame with
Paste Into content, so with Frame Edges on it drew the nested image's whole
outline out past the shape and never the frame's own edge. Only real groups
are walked now; a frame with nested items draws its own path, as text and
graphic frames do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 00:50:42 +06:00
.CooCoo e16785ea50 Fix CLI build warnings on Windows (#2356) 2026-11-05 09:53:17 -05:00
AxelandClaude Opus 5.5 0e85e9e5ec Esperanto as a text language (#1142) (#1204)
Esperanto in Character › Language (panel, Properties, style options),
mapped to the BCP 47 tag eo, with English-style typographer's quotes.
IDML keeps the name as written ($ID/Esperanto). Not hyphenated yet:
morpheme-based hyphenation follows in #1201.


Claude-Session: https://claude.ai/code/session_01Y9j7XJDCmPc8UZLs9cTxtu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:16:42 +01:00
aiongg 8aceec1f08 Merge pull request #2348 from Hamza0101/fix/copy-keeps-anchored-frame-text
Copy: a text frame anchored in a copied frame keeps its text
2026-11-05 06:59:03 -05:00
aiongg 97e75f8fc4 Apply review fixes
- crates/engine/src/cmd/edit.rs
2026-11-05 11:42:55 +00:00
Hamza 3c8a3230ff Copy: the anchored-frame scan skips a copy that keeps no story, and reads each anchored item once
From review: a copied graphic keeps no story, so there is nothing to scan; and anchored_items already lists a group's members, so walking each item again visited them twice through unbounded recursion. A text frame inside an anchored group (as IDML import anchors an InDesign group) is tested.
2026-11-05 16:20:38 +05:00