417 Commits
Author SHA1 Message Date
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
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
4f3bb5340e Pages: parents and alternate layouts; export and print layouts and versions (#1342)
* Pages: more parents and alternate layouts as columns in the Pages panel

Parents
- layout.parents.duplicate copies a parent spread (pages and items) under the next free prefix.
- layout.parents.options also renames the prefix (1-4 characters, no "-") and the name; basedOn is now optional.
- layout.parents.new validates prefix and name and takes basedOn.
- Pages panel: a "+ New Parent" row adds B, C… in one click. The parents' context menu and the panel menu have New Parent…, Duplicate Parent Spread, Parent Options… and Delete Parent, named after the selected parent, or a submenu of the parents when none is selected. A new Parent Options dialog has Prefix, Name and Based on Parent.

Alternate layouts
- Section.layout marks the section that starts a layout; Document::layouts() lists them.
- layout.createAlternate copies one layout (from), not every page; new layout.alternate.duplicate, .delete and .options (rename, resize).
- Each layout starts a spread of its own; sections now follow their pages when pages are inserted or deleted.
- Pages panel: with more than one layout, each is a column side by side with a header (name, size, ▾ menu: create, duplicate, options, delete) and a + column to add one.

Tests cover the commands' happy and error paths, undo, serde, and the panel's menus and columns.

* Pages: address review of parents and alternate layouts

- Deleting a layout's first page keeps the layout's name when another section lands on the same page.
- A parent's full label (prefix-name) must be unique; parents may still share a prefix.
- Alternate layout names round-trip through IDML (Section AlternateLayout / AlternateLayoutLength).
- The Pages panel's selected parent is per document and no longer saved in ui.json.
- Layout ▸ Parent Options… and Alternate Layout Options… open the Pages panel's dialogs.
- The Pages panel computes the layouts once per frame.

* Export: layouts and versions in one export, with presets; print chosen layouts

- file.exportBatch writes the chosen alternate layouts (a file per layout, or all in one PDF) in several versions at once: PDF with any marks, bleed and standard, PNG or JPEG at a resolution. Each version goes in a folder of its own, and file names come from a template ({document}, {layout}, {version}, {date}). Names are made safe and unique. dryRun lists the files; without a dir the files come back as base64 (web).
- Export presets: file.exportPresets, file.saveExportPreset, file.deleteExportPreset, kept in prefs.json. Built-ins: Print + Screen, Press PDF per Layout, Images per Layout, All Layouts in One PDF.
- file.print takes layouts and separateJobs (a job per layout, titled after it).
- File ▸ Export Layouts and Versions… dialog: preset, layouts, versions (add from templates, duplicate, remove, quick PDF choices or custom marks), name template, and a live list of the files it will write. It opens from a layout column's ▾ menu as "Export Layout…". The desktop asks for a folder; the web downloads each file.
- The Print dialog prints chosen layouts, together or separately; the Export PDF dialog fills the page range from a layout.
- crate::io::create_dir_all keeps folder creation inside the automation write root.

* Export: address review of layouts and versions

- An exported image is capped at 80 million pixels, the limit EPUB page images already use.
- The Print dialog stays open and says why when no layout is chosen.
- Clicking a layout column's header leaves Edit Parents before going to the layout's first page.

* Pages, Export: address maintainer review of layouts and versions

- Sections (and the layouts they start) follow a moved page, as they do inserted and deleted
  ones: moving a page across a layout boundary no longer moves the other pages between layouts.
  Delete and move share `settle_sections`.
- Alternate Layout Options resizes only pages whose size changes: renaming a layout no longer
  forces every page to the first page's size.
- Export file and folder names that are Windows device names (CON, NUL, COM1…) get a leading `_`.
- An export without `dir` refuses past 512 MB of files instead of building them all in memory.
- A file that fails partway through an export to a folder says how many were already written.
- Parent Options and Print keep the dialog open, with what was typed and why, when the engine
  refuses.
- A duplicated layout's default name stays within the 100-character limit.
- rustfmt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pages: Alternate Layout Options sends the size only when it changed

The dialog starts at the layout's first page's size, so the engine couldn't tell a rename from a
resize to that size. The dialog now leaves out a width or height still at its starting value, and
`layout.alternate.options` resizes every page of the layout when a size is given, as before.
The test moves to the UI, where the size was coming from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 06:02:27 +01:00
aiongg f3e35af8bf Merge pull request #1366 from con3code/fix/idml-q-unit
IDML: Q ruler units are Q, as InDesign writes them
2026-11-03 12:59:48 -05:00
Brandon Thomas 0cdac69e66 Merge pull request #2174 from Silvertree2010/fix/2173-bug-report-designcraft-export-ta
Keep style export tags and classes on IDML import and export
2026-11-02 23:23:28 -05:00
Dylan M. Taylor 74eab18d16 Assets: index placed files by content so importing many graphics stays linear (#2181) 2026-11-02 22:51:17 -05:00
NetterSilverandClaude Opus 5.5 7d1340a3df Keep style export tags and classes on IDML import and export
Fixes #2173

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:09:48 +01:00
Brandon Thomas 99f90cbe2b Merge storytold/main into PR #2052 2026-10-31 01:02:00 -04:00
Brandon Thomas 400e96c200 Merge storytold/main into PR #2052
# Conflicts:
#	crates/ui-egui/src/dialogs/tests.rs
2026-10-31 01:00:57 -04:00
Brandon Thomas faa2bf5089 Merge storytold/main into PR #1239
# Conflicts:
#	crates/doc/src/xref.rs
2026-10-31 01:00:56 -04:00
texjer 06d29fe3a6 Parents: single-sided pages in a spread show the parent page in their place 2026-10-31 00:04:41 -04:00
aiongg 4b29760ffd Merge track 6
Conflicts: dialogs.rs dialog widths (both kept); i18n/*.rs adjacent rows (both kept).
2026-10-30 23:57:22 +00:00
aiongg aae929eb97 Merge track 3 2026-10-30 23:56:48 +00:00
aiongg 1fb973c3ad Merge PR #1514: Package dialog
crates/ui-egui/src/dialogs.rs: kept main's Insert Pages dialog and the PR's MULTILINE_FOCUS key
crates/ui-egui/src/i18n/*.rs: kept both sides' rows
2026-10-30 23:27:02 +00:00
aiongg 35697cb807 Merge PR #1255: TOC separator and style notation
crates/idml/src/tests.rs: kept the PR's TOC tests, dropped the duplicate copy from #1249
crates/engine/src/tests/idml.rs: main's createBookmarks plus the PR's right-indent separator
crates/ui-egui/src/dialogs.rs: main's labelled_by fields plus the PR's special-character menu
crates/ui-egui/src/i18n/*.rs: kept both added rows
2026-10-30 23:26:42 +00:00
aiongg d00e7fce21 Merge PR #1249: IDML import of TOC separators and entry styles
crates/idml/src/import.rs, tests.rs: conflicts were the old pre-split copy; PR's three small changes ported by hand into import/document.rs, names.rs and tests.rs
2026-10-30 23:26:07 +00:00
aiongg 89ef0e3d81 Merge PR #1606: Paragraph Shading and Border pages
crates/ui-egui/src/panels/properties.rs, properties/object.rs: main's split module kept; preview_row and paint_corner_shape exported for the shading corner menus
crates/ui-egui/src/panels/properties/tests.rs: both sides' tests kept
crates/ui-egui/src/dialogs/tests.rs: main's tests kept; the border test uses the new corner labels; the shading layout test added
crates/ui-egui/src/i18n/*.rs: union of keys without duplicates
crates/ui-egui/src/dialogs.rs: the corner shape face uses corner_shape_button (no hard-coded colour in the dialog)
2026-10-30 23:17:40 +00:00
aiongg 0e4cb1f294 Merge PR #1451: Effects blend modes for Drop Shadow, Inner Shadow and Outer Glow
crates/ui-egui/src/effects_dialog.rs: kept both the angle dial tests (main) and the blend mode test (PR); the stray 'The dialog's body.' doc line moved back onto show.
2026-10-30 23:16:03 +00:00
moppeeer 3711ac05ba Merge remote-tracking branch 'origin/main' into effect-blend-modes
# Conflicts:
#	crates/render/src/fx.rs
2026-10-30 21:57:48 +01:00
aiongg 4638b19387 Merge PR #1257: TOC: generate and refresh PDF bookmarks
crates/ui-egui/src/dialogs/tests.rs: kept HEAD, appended #1257's new test
2026-10-30 19:46:49 +00:00
aiongg 19a8a7ae66 Merge PR #1463: Paragraph rules: Type, Gap Color and Tint, Keep In Frame
crates/ui-egui/src/dialogs/tests.rs: kept both sides' appended tests
2026-10-30 19:44:13 +00:00
Claude 239ac1bbb2 Cross-references: saved text counts as generated only on an exact match
An imported cross-reference's text is regenerated only when it is exactly what its format
generates for its destination (Document::drop_generated_xref_text, once the destinations are
resolved); "Page 7, revised" is an edit and is kept, as is the text of one whose destination is
gone. The loose in-order match (matches_format) is gone. Page numbers are recognised by the
instance's AssociatedTextVariable (a cross-reference page number variable, or InDesign's name
for it when the file doesn't define it) rather than its Name, and text inside tracked-change
wrappers counts as shown (insertions in, deletions out). Export writes generated text with its
page numbers as the live variable too, so it reads back as generated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-30 17:02:17 +00:00
Claude d48b650320 Merge storytold/main into xref-edited-text
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa

# Conflicts:
#	ROADMAP.md
2026-10-30 16:36:59 +00:00
texjer 4461ebd886 Import warnings: Show selects the frame a story warning is about 2026-10-30 11:50:41 -04:00
moppeeerandClaude Opus 5.5 5b52b23b00 IDML export: the paragraph box overprints in their own function
Keeps para_box_attrs within clippy's cognitive complexity limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:07:37 +01:00
Brandon Thomas 4777269ee6 Merge pull request #1976 from hscabral/idml-export-story-preference
IDML export: write the document's default StoryPreference
2026-10-29 21:52:04 -04:00
Brandon Thomas cddf074cc4 Merge pull request #1868 from Hamza0101/fix/table-allocation-cap
Tables: a shape a table cannot hold is clamped, not allocated (#1583)
2026-10-29 21:51:53 -04:00
Brandon Thomas 1f04ec6d61 Merge pull request #1915 from Silvertree2010/fix/h3022-idml-export-duplicates-cross-ref
Fix duplicated xref format text before an unclosed '<' in IDML export
2026-10-29 21:51:42 -04:00
moppeeerandClaude Opus 5.5 be7861364b IDML export: the border gap overprint through main's attribute macro too
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:57:14 +01:00
moppeeerandClaude Opus 5.5 0268dd40e7 IDML export: paragraph overprint attributes through main's attribute macros
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:57:07 +01:00
moppeeerandClaude Opus 5.5 83aa235cbc TOC import: the listed style reference shared from main's split import styles module
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 15:30:31 +01:00
Hamza 18f2dc2bde A spent cell budget stops reading tables, rather than keeping one each
Keeping a 1 x 1 table for every further `<Table>` was unbounded in the one
direction the budget exists to close. `<Table></Table>` is fifteen bytes, so
the 512 MiB a part may decompress to holds some 36 million of them, and a
`Cell` is 1,360 bytes: tens of gigabytes, with every table inside both caps.
The test I wrote said as much without my noticing -- its invariant was
`reserved <= MAX + tables`, and `tables` had no bound.

`budgeted_size` now returns `None` once the budget is spent and the table is
not read at all, so the cells one import reserves are at most the budget,
exactly. The invariant is now `reserved <= MAX_IMPORT_TABLE_CELLS`, with no
term that grows with the file.

The warnings needed the same treatment, for the same reason: a file of 400,000
tables each claiming one row and two columns got a line about every one of
them. At most 20 are named, then one line saying the rest were the same, and
the file is told once that its tables ran out of cells between them.
2026-10-29 16:43:49 +05:00
moppeeer e16e0c4d61 Merge remote-tracking branch 'origin/main' into paragraph-rule-types
# Conflicts:
#	crates/compose/src/lib.rs
#	crates/doc/src/attrs.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
2026-10-29 12:08:24 +01:00
moppeeer 165d8c65ac Merge remote-tracking branch 'origin/main' into paragraph-shading-indesign
# Conflicts:
#	ROADMAP.md
#	crates/doc/src/attrs.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-29 11:50:25 +01:00
moppeeer 94dad76ac1 Merge remote-tracking branch 'origin/main' into codex/toc-right-indent-separator
# Conflicts:
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
2026-10-29 11:44:26 +01:00
moppeeer c0b4746c8d Merge remote-tracking branch 'origin/main' into codex/toc-pdf-bookmarks
# Conflicts:
#	crates/engine/src/cmd/style.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-29 11:44:22 +01:00
moppeeer b6fec896f9 Merge remote-tracking branch 'origin/main' into effect-blend-modes
# Conflicts:
#	crates/doc/src/item.rs
#	crates/idml/src/export.rs
#	crates/idml/src/import.rs
#	crates/ui-egui/src/effects_dialog.rs
2026-10-29 11:42:14 +01:00
Henrique CabralandClaude Opus 5.5 50b5936b78 IDML export: write the document's default StoryPreference
InDesign always writes a document-level StoryPreference in
Resources/Preferences.xml; DesignCraft only wrote one per story. IDML readers
that take the spread reading order from the document's preference, such as
Okapi's IDML filter, failed on DesignCraft's files without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:07:42 -04:00
Claude 97b42e3476 Cross-references: the edited text's IDML export moves into its own method
Keeps story_para under clippy's cognitive-complexity limit, which upstream now enforces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-28 19:03:09 +00:00
Claude 11f4364705 Merge storytold/main (v0.14.0) into xref-edited-text
The IDML import and export of edited cross-reference text move to import/story.rs and
export/story.rs with upstream's split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-28 18:09:43 +00:00
Dylan M. Taylor be9b8f360e Refactor idml for strict Clippy checks 2026-10-28 11:39:26 +00:00
Hamza 1995fb5feb IDML import: the tables in one file are capped between them
Review pointed out that capping each table leaves a file free to hold
many capped tables: three at the per-table limit claim 750,000 cells,
about 1 GB of Cell, with every table inside its own limit. The package
reader's limits count input bytes, which say nothing about how many
cells a row and column count claim.

budgeted_size clamps each table against what the import has left, and
the cells are reserved before Table::new so a cell's own story cannot
spend the budget the table around it is counting on. The budget is
400,000 cells: a Cell carries a whole story and is 1,360 bytes, so that
is about the 512 MiB one part of the file may already decompress to,
and the same count the spreadsheet importer collects at most.

A table is never read as nothing. One row and one column are kept, so
the table stays in the document and still says where its text went, and
a file pays one cell for a table it really does contain -- which its own
size already bounds. The warning says which limit cut the table down.

The arithmetic is tested rather than the allocation: 400,000 cells is
544 MB and three such tables are not a thing to ask CI to allocate. The
sequence test runs 32 claimed sizes, including usize::MAX, and holds the
invariant across all of them.
2026-10-28 16:31:34 +05:00
Hamza b49535624c Tables: a shape a table cannot hold is clamped, not allocated (#1583)
`Table::new` allocates `rows * cols` cells up front, and the counts reach it
from IDML and DOCX, where they are whatever the file claims. Measured here:
`size_of::<Cell>()` is 1,360 bytes, so the reported IDML case -- one `<Table>`
with 5,000 `<Row/>` and 5,000 `<Column/>`, about 1 KB zipped -- asks for 25M
cells, some 34 GB. That allocation does not fail politely: it aborts the
process through `handle_alloc_error`, which no `catch_unwind` can catch, so
every open document's unsaved work goes with it.

A table is clamped to InDesign's own limits, 10,000 rows and 1,000 columns,
and to 250,000 cells whatever its shape -- the row and column limits alone
would still allow 13.6 GB. Columns are clamped first and rows take what is
left, because a table is far more often long than wide.

The IDML importer walks the clamped slices and reports the difference.
Indexing its rows and columns by the file's own counts would be out of
bounds, which is how a 34 GB abort would otherwise have become a panic.

Rebased onto the post-split main: the constants and `clamped_size` live in
`doc`'s `table` module root, `Table::new` in `table/grid.rs`, and the IDML
table reader in `import/table.rs`.
2026-10-28 16:26:13 +05:00
NetterSilverandClaude Haiku 5.5 e8c0a19180 Fix duplicated xref format text before an unclosed '<' in IDML export
Found while testing crates/idml/src/export/designmap.rs:208-230 (xref_format_el)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 11:26:46 +00:00
aiongg f33de8f4aa refactor(pdf): split export.rs into export/ 2026-10-26 21:00:23 +00:00
aiongg bec8800b62 refactor(idml): split export.rs into export/ 2026-10-26 21:00:23 +00:00
aiongg 2c0882c6e0 refactor(idml): split import.rs into import/ 2026-10-26 21:00:23 +00:00
aiongg 3c19a24e68 Merge pull request #1374 from aiongg/print-color
Print color: overprint, knockout, CMYK blending and spot gradients through craft-pdf
2026-10-25 21:58:20 -04:00
Dylan M. Taylor 63a4f687fb Reduce cognitive complexity in UI dialogs, canvas, chrome, shortcut import and IDML export (#1848)
* refactor(ui): split shortcut set import into sort, apply and conflict steps

Resolves SonarCloud rust:S3776 on import (ui-egui/src/shortcut_import.rs).

* refactor(ui): split drop caps and nested styles dialog into row helpers

Resolves SonarCloud rust:S3776 on drop_caps_and_nested_styles (ui-egui/src/dialogs.rs).

* refactor(ui): split draw_guides into bleed, grid, overlay and guide helpers

Resolves SonarCloud rust:S3776 on draw_guides (ui-egui/src/canvas.rs).

* refactor(ui): split control_paragraph into alignment, indent, drop cap and shading helpers

Resolves SonarCloud rust:S3776 on control_paragraph (ui-egui/src/chrome.rs).

* refactor(idml): move graphic layer option tree out of image_el

Resolves SonarCloud rust:S3776 on image_el (idml/src/export.rs).
2026-10-25 21:25:57 -04:00