Commit Graph
296 Commits
Author SHA1 Message Date
Brandon Thomas 5442d648eb Merge pull request #1193 from Silvertree2010/fix/h2418-idml-import-accepts-nan-inf-in-s
Ignore NaN and infinite numbers when importing IDML
2026-10-19 02:50:02 -04:00
Brandon Thomas 54052d77f9 Merge pull request #1237 from aykutfirat/fix/cell-first-baseline
Tables: cell First Baseline offset (Cell Options › Text › First Baseline)
2026-10-19 02:49:33 -04:00
Brandon ThomasandClaude Opus 5.5 8981d5e4cd IDML import: ignore non-finite Minimum First Baseline offsets, clamp to 0..8640
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 02:31:31 -04:00
Aykut FiratandClaude Opus 5.5 08ce444054 Tables: cell First Baseline offset (Cell Options › Text › First Baseline)
Cells and cell styles had no first-baseline setting, so every cell set its first line by the
Ascent offset (the font's typographic ascender). IDML documents that set FirstBaselineOffset on a
cell style (CapHeight is common, often through a table style's body region) composed every row
taller than the document asked for. With a font whose typographic ascender is a full em (Avenir),
each row grew about 2.6 pt and long tables overset, losing their last rows.

- doc: Cell.first_baseline / first_baseline_min; CellStyle.first_baseline / first_baseline_min,
  applied by CellStyle::apply_to and carried through resolve_cell_style's BasedOn chain.
- idml: FirstBaselineOffset and MinimumFirstBaselineOffset read and written on cells and cell styles.
- compose: a cell's text frame uses the cell's first baseline offset and minimum.
- engine: table.setCell, style.cell.create/edit take firstBaseline / firstBaselineMin; table.get
  reports them; style.cell.create fromSelection captures them.
- tests: import through BasedOn and a table style's body region, a cell's own override, IDML round
  trip; composer cap height vs ascent and a fixed minimum; command parameters and validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:07:46 -04:00
skylar taylor-barrick 9fd0a881f7 Merge storytold/main into linked-path-checks
The conflict was in links.rs, where main's atomic relink work (b050343a) and
this branch's path gate both changed `update`. Kept main's mechanism (build the
replacement with relinked_asset, then insert it) and this branch's gate (a link
craft-docpath refuses is missing and never read).

relinked_asset now takes the path to read and the link to record separately.
Previously they were one argument, safe only because update passed the raw stored
link; passing the resolved path to the old signature would have rewritten every
relative link to an absolute one on each refresh.

Also passes None for the document in main's embedded-status test, where the asset
has no link so the document is irrelevant.
2026-10-17 12:39:14 -06:00
NetterSilverandClaude Haiku 5.5 ec4fbb270f Ignore NaN and infinite numbers when importing IDML
Found while testing crates/idml/src/import.rs:2748 (stroke_from: StrokeWeight, MiterLimit), also El::num in crates/idml/src/xml.rs:96 and the plain `.parse().ok()` calls around import.rs:1833, 2823 and 2832

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 06:20:21 +02:00
skylar taylor-barrick 0d541deab7 Merge upstream/main into codex/fix-relink-eps
Resolve 12 conflicted files. Where both sides changed the same code:

- format: keep main's bounded-read load_zip (MAX_ENTRIES, read_entry) and
  this branch's u64 version reporting, so a version above u32::MAX is named
  as written rather than clamped. Both sides had fixed the same truncation.
- render: main's natural_size supersedes image_natural_size (it already
  reads an EPS bounding box and also handles PDF pages); callers in table
  and data merge now use it.
- images: keep the DSC-aware header scan and (atend) trailer lookup, take
  main's classic-Mac line endings, and carry its overflow guard into
  parse_box. eps_proxy draws main's simple rectangles before falling back
  to the placeholder, still over a retained EPS source.
- engine/file: take main's placement shape (add_asset, crop box geometry,
  place cursor, finite_rect, object effects) and keep this branch's
  validate_placement in both branches; edit() works on a clone and commits
  only on Ok, so validating inside it stays all-or-nothing.
- engine/links: keep building the replacement before edit() so a file that
  cannot be read leaves the document untouched, and move main's crop_pdf
  refresh into relinked_asset, which both relink and update go through.
- engine/export: EPS preview warnings join the font-embedding warnings
  rather than shadowing them.
- idml: an EPS element keeps ActualPpi (its pixels are a preview raster);
  main's PDF exclusion is unchanged.

Cargo.lock regenerated. Tests and doc strings from both sides kept.
2026-10-16 19:57:08 -06:00
aiongg 248e10908b Follow paths stored in documents only on this computer or the document's share
Links, data sources, IDML package links and book chapters go through
io::document_path (craft-docpath) before they reach the file system:
relative paths resolve beside the document, and paths to other computers,
devices and URLs are missing, with the reason in links.list and Preflight.

IDML and INDD packages read a linked file where its link points on this
computer, then from the link's trailing names beside the package, only
when it is a regular file up to 256 MB holding an image or a graphic. A
file URI that names a host keeps it as a network path.
2026-10-16 17:05:09 +00:00
aiongg 4ebcd78a03 Integration: #1027 test passes the string slices without a needless borrow (clippy) 2026-10-16 04:21:33 +00:00
aiongg 52cf755179 Merge PR #1070: Table of Contents: page number and separator character styles, Sort Entries in Alphabetical Order
crates/engine/src/tests_idml.rs: keep both new tests.
crates/ui-egui/src/i18n.rs: keep both sides' rows.
2026-10-16 04:10:56 +00:00
aiongg 6e4f122b8c Merge PR #1100: Placed greyscale and bitmap images take the swatch applied to them (IDML, screen, PDF)
Graphic literals (cli perf, doc assets/item, engine datamerge/export/file/links/mathexpr/media/table/sample, epub, format, pdf, pptx, render, tools, ui-egui properties, and tests): name the branch's clip and wrap (#561, #381) and the PR's fill.
crates/doc/src/item.rs: Graphic keeps wrap and gains fill.
crates/idml/src/import.rs: read the image swatch, then the branch's styled FrameFittingOption lookup; the graphic keeps its wrap.
crates/idml/src/tests/assets.rs: keep both new tests.
crates/render/src/lib.rs: keep #362's crop-box sizing of placed PDFs and colour the resulting pixmap.
crates/render/tests/colorize.rs, crates/epub/tests/grouped_reading_order.rs, crates/compose/src/tests.rs: literals name the branch's Asset and Graphic fields.
crates/pdf/src/export.rs: the soft mask interpolates by the standard's raster mode (the branch has no per-exporter interpolate switch).
docs/gaps.md, docs/target-app-parity.md: status lines list both sides' changes; gap text keeps the clipping-path items and adds the image swatch.
2026-10-16 04:07:57 +00:00
aiongg 499f5f27c6 Merge PR #1001: Importers: XML element nesting and DTD group nesting are bounded (#712)
crates/engine/src/dtd.rs: keep the branch's version; #319 already bounds DTD group nesting (with the same limit of 64) along with the entity budget and matching fixes, so this PR's DTD part is dropped. The XML element limits are taken.
2026-10-16 04:03:53 +00:00
aiongg 4aeda523b6 Integration: #1027 composite-font scales are checked after the percent conversion 2026-10-16 04:03:45 +00:00
aiongg 910c59f108 Merge PR #1027: idml: one unusable CJK metric no longer refuses the whole file
crates/idml/src/import.rs: keep the branch's strict Mojikumi row reader (#129), which refuses a non-finite value; take the PR's composite-font filters.
crates/idml/src/tests.rs: the PR's test drops its Mojikumi NaN row, which #129 refuses on purpose.
2026-10-16 04:03:35 +00:00
aiongg 174ff23f17 Merge PR #1010: IDML: preserve liquid guides on document and parent pages
crates/idml/src/lib.rs: crate doc keeps the branch's feather list and adds liquid guides.
crates/idml/src/tests.rs: keep all test modules from both sides.
docs/file-format-parity.md: keep both change-log rows; status line names both changes.
ROADMAP.md: drop the blank line the union merge left inside the progress log, and a stray one further down.
2026-10-16 04:03:02 +00:00
aiongg f419fa2a7a Integration: exported ruler guides are measured from the exported ruler origin
next writes the document's ruler origin (Spread, Page or Spine) to IDML; main
measured guide locations from the spread's left edge, as for SpreadOrigin only.
A page-origin document's guides came back one page width to the right. The
round-trip test covers all three origins.
2026-10-16 03:37:26 +00:00
aiongg 18ae8e4761 Merge upstream main a184db6f8 with our 15 PRs (batch-0) into next
Conflicts resolved per file (both sides kept unless noted):
crates/engine/src/atomic.rs | 1 | main's: `write_file` removed (main routes command writes through `crate::io::write_file`); its one caller in file.rs now uses `crate::io::write_file`
crates/engine/src/cmd/datamerge/fill.rs | 1 | next's `Graphic` literal (has `clip` and `wrap`, which next's `Graphic` carries)
crates/engine/src/cmd/datamerge/mod.rs | 1 | main's `read_path_capped` (crate::io metadata/read_capped, roots checked); next's capped read is the same behaviour
crates/engine/src/cmd/datamerge/parse.rs | 1 | main's (`crate::io::metadata`, `crate::io::Metadata`)
crates/engine/src/cmd/edit.rs | 1 | next's `spread_param(p, "edit.paste")` (the second argument is the command id on both sides)
crates/engine/src/cmd/export.rs | 3 | main's: EPUB params doc, `toc_title` once (main's position; next's duplicate dropped)
crates/engine/src/cmd/file.rs | 7 | place doc: next's (lists page/rect/autoflow params #803 checks); next's recent-file functions + main's `foreign_document`; next's open (load_bytes, warnings, non-DesignCraft refusal) with main's checked `crate::io::read_file` for the bytes; main's `check_write` be
crates/engine/src/cmd/find.rs | 7 | main's Find scope (FindContext, scope/selection/story, continuation, UTF-8 repair) on next's `designcraft_compose::grep::Grep` (dialect GREP): find uses `find_all` capped at MAX_MATCHES, change uses `replacements`/`find_all` (literal change text not expanded), fin
crates/engine/src/cmd/fonts.rs | 1 | next's (`has_style`, resolved/replacement face); `has_style` already compares styles ignoring spacing and case, which is main's `same_style` fix; main's test kept
crates/engine/src/cmd/interchange.rs | 1 | next's `import_idml_report_with_previews` (previews for unreadable links)
crates/engine/src/cmd/layout.rs | 8 | pages.insert: main's overflow-safe `after` + next's checked `parent_ref`; main's duplicateSpread doc; layout.section: next's `SectionEdit` (already keeps unnamed settings, #717) with main's `d.repaginate()` moved into `SectionEdit::apply` (remove and set paths),
crates/engine/src/cmd/links.rs | 5 | next's links.list (no doc clone); main's `text_status` via crate::io; `use std::io::Read` dropped (unused after main's read_capped); test literals main's. `link_missing` (next) uses `crate::io::exists` for the file-guard scan
crates/engine/src/cmd/object.rs | 4 | object.fill/stroke tint-alone: main's (23e1680e7; out-of-range tint is an error, which main's test asserts); object.fit doc: next's (lists `axis`)
crates/engine/src/cmd/paths.rs | 1 | next's (shatai transform on outlines)
crates/engine/src/cmd/place_text.rs | 1 | next's `add_flow_frame` (used by autoflow)
crates/engine/src/cmd/preflight.rs | 2 | next's overset/unplacedStory issue (its `overset_location` already applies the parent transform main's #738 fix adds); next's two test modules kept with main's
crates/engine/src/cmd/scripts.rs | 1 | combined: next's `in_undo_step = false` (#406) + main's full rollback of a failed script (#709)
crates/engine/src/cmd/style.rs | 14 | param docs next's (gradient swatch.create, swatch stops, model colours); create_para next's; rename_style main's structure (rename entry, then `redirect_style_refs`), redirect has next's full reference set (cells, defaults, notes, TOC) + main's never-based-on-it
crates/engine/src/cmd/text.rs | 10 | main's selectSentence/Paragraph/Story + select_around; next's selectionAttrs (mixed) + main's openTypeFeatures/type.feature; main's comments; tracked typing: next's `reserve_command_text` + main's `insert_format_at`; empty-paragraph mark formatting main's (with `
crates/engine/src/cmd/thread.rs | 1 | next's `spread_param(p, "text.thread")`
crates/engine/src/dtd.rs | 1 | next's linear, depth-bounded `expand_parameter_entities` with main's budget (input + 1 MiB, `MAX_EXPANDED_BYTES`) and message ("expand the DTD beyond"); 16 MiB constant removed; both sides' tests pass on message
crates/engine/src/guard.rs | 2 | main's whole-transaction snapshot (docs, active, journal; #742) + next's `in_undo_step` restore; tests both
crates/engine/src/lib.rs | 5 | LoadedGraphic doc next's; next's `new_document_preview`; execute: main's `discard_stale_find_scope()` before next's nested-undo run; modules both (tests_autosize, tests_file_guard, tests_params, tests_roots)
crates/engine/src/recovery.rs | 1 | next's test (superset of main's assertion)
crates/engine/src/tests.rs | 4 | moved tests deduplicated (identical on both sides); next's preflight and direct-selection/kerning tests + main's style-delete, locked-frame and baseline-fit tests
crates/engine/src/tests_idml.rs | 3 | main's order (drop cap and margins tests identical on both sides, once each)
crates/ui-egui/src/canvas.rs | 3 | next's screen_options/DisplayKey block and tests kept; two tests that auto-merge had added twice (parent_guides_on_a_page_are_not_picked_there, baseline_grid_lines_are_the_ones_on_the_page) kept once; main's `typing`/`screen_at`/`click` helpers and its tests clicks
crates/ui-egui/src/control.rs | 3 | main's `count` check and pending budget (control queue + synthetic queue); exact-budget test runs both sides' cases (click 255 on 9,492 pending; drag 9492×255)
crates/ui-egui/src/cursors.rs | 1 | main's (same Pen cursor set)
crates/ui-egui/src/dialogs.rs | 14 | dialog body (newDocument … alert match) is next's text, with main's Find/Change scope reset (`set_field`, `reset_find_scope`, `find_change_params`) and main's Create Guides / Margins and Columns arms ported in; `Dialog::new` ruby section (next) + main's `set_fi
crates/ui-egui/src/i18n.rs | 7 | ka and pl modules, languages, statics and lookups both; `category_first` covers uk, ru, pl, ka and `count_label` keeps next's Czech plurals; shared TABLE union; next's duplicate rows for Auto, Number:, Gutter:, Top:, Bottom:, Inside:, Outside:, Left:, Right: dropped
crates/ui-egui/src/i18n/ar.rs | 2 | union of rows, no duplicate keys
crates/ui-egui/src/i18n/it.rs | 1 | union of rows, no duplicate keys
crates/ui-egui/src/i18n/ja.rs | 1 | union of rows, no duplicate keys
crates/ui-egui/src/i18n/pt_br.rs | 1 | union of rows, no duplicate keys
crates/ui-egui/src/i18n/uk.rs | 1 | union of rows, no duplicate keys
crates/ui-egui/src/icons.rs | 2 | both tests (main's typography icon, next's distribute icons)
crates/ui-egui/src/lib.rs | 11 | UiState: next's tabs_panel + main's last_color/last_gradient; `cursors` field once; next's SVG paste and paste-on-spread-in-view; next's extensionless document open; opens_as_document next's comment; tests both (next's control bar / key-name tests, main's rich black
crates/ui-egui/src/menus.rs | 15 | app.language doc next's text with pl and ka; UI_COMMANDS both (next's Tabs, Toggle Presentation, Apply None text; main's panel commands, Apply Color, Apply Gradient); Edit › Interface Language stays out (next moved it to Preferences); Type menu main's order with
crates/ui-egui/src/panels/glyphs.rs | 1 | next's (already has main's scroll-bar fix); the scroll-bar test module auto-merge had added twice kept once
crates/ui-egui/src/panels/layers.rs | 3 | next's drag-and-drop `object_rows`, with main's features ported into it: selection reveal (layer opens, row scrolls into view once), selected rows highlighted (also a group whose member is selected), double-click rename in place (`object.rename` queued like
crates/ui-egui/src/panels/mod.rs | 6 | tabs + typography modules and color_panel_tests; SelInfo has wrap_side/wrap_contour and wrap_offsets (offsets from the graphic's own wrap when it is selected); next's swatch_picker returning the chip response; next's content_selection_tests
crates/ui-egui/src/panels/pages.rs | 1 | next's (parents dragged by id)
crates/ui-egui/src/panels/properties.rs | 4 | next's swatch popup (Apply To, Tint); main's four wrap offsets then next's Wrap To / Contour Type; test fixtures main's (`..Default::default()`, no unused builder)
crates/ui-egui/src/panels/swatches.rs | 3 | main's Delete Swatch button + next's New Swatch comment; next's draggable rows; click: main's PICKED + fill/stroke in front, with next's no-apply on double-click
crates/ui-egui/src/toolbar.rs | 2 | main's Apply Color / Gradient / None wells and functions; next's apply_none doc line
crates/compose/src/lib.rs | 5 | next's line loop kept (mark_leading + last_line_mark, wrap segments/carry, grid gyoudori); main's mark-chars base_leading block (auto-merged) stays beside it. `fits`: next's #106 rule (baseline fit for horizontal frames, full descent for paths, vertical text, footnote
crates/compose/src/tests.rs | 5 | next's tests (tombstone set, sliver/one-sided/contour/clipping wrap tests, borders, lists, kenten, grid, anchored wrap, balance, shaped frames) plus main's an_empty_last_paragraph_isnt_overset; main's wrap_object_beside_one_narrow_column_leaves_the_other_alone re-ad
crates/doc/src/assets.rs | 1 | main's doc comment (same meaning)
crates/doc/src/build.rs | 3 | parent_page_for: next's parent_page_for_side (RTL-aware); single_sided_page_shows_first_parent_page test kept once, at main's position
crates/doc/src/item.rs | 1 | next's rotated_group_bounds_follow_the_children_outlines test kept
crates/doc/src/lib.rs | 1 | next's kerning_increment field kept; main's "Bulleted & Numbered" wording
crates/doc/src/page.rs | 1 | next's outer_page_indices + parent_page_for_side (integration of parent-margins with rtl-master-sides); main's parent_page_index dropped, its only caller (build.rs) uses parent_page_for_side
crates/doc/src/slice.rs | 5 | extract: next's `marked()` (get-based, fix_notes/fix_endnotes/fix_marks; main's version indexed slices and could panic); insert_story: main's take() limit on endnote/editorial payload copies with next's id comment, redundant `let ne`/`let nn` dropped; main's excess_note
crates/geom/src/corners.rs | 1 | next's box_outline_rounds_only_closed_corners test kept
crates/render/src/damage.rs | 1 | next's wrapping-anchored-object damage test kept
crates/render/src/fx.rs | 2 | feather: next's choke/corners sigma+join inside main's structure (body and clip at device resolution, with_filters(.., None)); inner glow centre: next's blend `mix` with main's clip argument (bp, xf)
crates/render/src/lib.rs | 3 | next's layer comment; next's front baseline grid draw after items; gradient match on g.kind (main's tuple scrutinee did not match the single-pattern arms)
crates/render/src/text.rs | 5 | paint_glyphs: main's paint_stroke closure structure with next's stroke overprint (overprinting helper, stroke `(TextPaint, bool)`); callers pass next's overprint_fill/overprint_stroke
crates/render/tests/placed_pdf.rs | 1 | main's `near` helper version
crates/tools/src/lib.rs | 1 | main's (PenConvert already declared below, avoids a duplicate variant)
crates/tools/src/pen.rs | 1 | main's module doc
crates/tools/src/select.rs | 1 | next's Drag::Corner variant (live corners) kept
crates/tools/src/tests.rs | 4 | next's tests (content drag, live corners, table border drag, eyedropper matchFrom, fitting handles, pen cursors once); eyedropper_ignores_inactive keeps next's samples_image check (main's exact-JSON compare lacks matchFrom) with main's `..Default::default()` Asset lit
crates/tools/src/text.rs | 2 | next's table border drag (BorderDrag, border_at) kept, then main's comment on locked text frames
crates/tools/src/xform.rs | 2 | main's explicit `Vec<&Item>` for shown children; next's eyedropper matchFrom (integration 796e9d713)
Cargo.toml | 2 | main's craft-pdf workspace dep added (craft-print stays once, in next's craft-libs block); [patch.crates-io] keeps next's winit (rust-windowing #4694 backport) and adds main's krilla patch; main's skylartaylor winit left out (see UNSURE 1)
Cargo.lock | 1 | union: next's craft-atomic and craft-cms entries plus main's craft-pdf entry; needs `cargo` normalisation
README.md | 1 | Open bullet combines INDD import via indd-utils (next) and editable PowerPoint export (main); main's paragraph break kept
contributors/people.toml | 1 | both people kept (switch87 from next, Nmarello from main)
apps/designcraft-cli/src/main.rs | 4 | next's `Export` enum dispatch kept; main's .pptx export added as `Export::Pptx` (run prints slides and warnings, script runs file.exportPptx); main's `--pdf-options` null-as-{} parse; usage lines list .pptx
apps/designcraft/src/main.rs | 3 | `startup.leave()` right after `app.ui` (main #380) and next's macOS `sync_movable` kept after the IME discard; next's Apple Events install kept; main's macOS Dock Quit `with_close_requested_on_terminate` block left out (needs the skylartaylor winit; UNSURE 1)
crates/fonts/src/fontdb.rs | 9 | FONT6 aliases (next) combined with #911 WWS names (main): ScannedFace and both CatalogEntry constructors carry `wws` and `aliases`; `canonical_family` falls back to WWS family names; `exact_style_traits` canonicalises then tries typographic, then WWS names (`named_st
crates/format/src/lib.rs | 1 | tests: both sides kept (next's r7a nullable-default tests; main's oversized ZIP entry/asset/entry-count tests)
crates/idml/src/import.rs | 11 | main's bounded `Package` reader under next's `import_idml_report_with_previews` (metadata read through `pkg.part`); next's `tab_list_repaired` kept; ruler origin: next's `settings.ruler_origin`, and main's guide import reads it (main's private `RulerOrigin` enum and
crates/idml/src/tests.rs | 6 | item-level union: all next tests plus main's new ones (ruler guides, package limits, CJK leading model, liquid page rules module, mojikumi); no test dropped
crates/mcp/src/backend.rs | 1 | test comment merged from both
crates/pdf/src/tests.rs | 1 | tests: both sides kept
crates/pdf/src/text.rs | 4 | next's (skew `tan`, cluster `start`, ruby overprint warning); main had no change here beyond base
crates/textimport/src/docx.rs | 5 | next's (ListCounter integration for list levels, page-break test position); main's side was the older form of the same code
crates/epub/src/lib.rs | 30 | non-test code is next's (StoryWriter holds img_src/images/no_alt/heading_ids; covers main's #787/#791/#782 fixes, plus #314 group transforms, anchored images in images_without_alt, endnote story skipped in the TOC); tests: next's set plus main's extra css_weight_style a
docs/control-protocol.md | 2 | next's rows (ui.paste, count 1–255, documentGrid) and wording
docs/file-format-parity.md | 1 | rows combined (next's ICC notes, main's TIFF indexed colour)
docs/gaps.md | 2 | status change notes unioned; Flex Layout removed from missing (main implemented it), next's Parent Options sentence kept
docs/localization-parity.md | 4 | Polish and Georgian rows; 5 languages beyond the twelve; file list includes cs and ka; both history rows
docs/roadmap.md | 1 | M1 next's (#186 fixed on next), M2 main's IME wording
docs/target-app-parity.md | 6 | next's status line; File formats row next's, UI/UX row main's (IME no longer failing); UI line next's plus main's Swatches detail; one Page tool line (main's fuller one); Load styles and main's fuller Pathfinder line both kept, main's duplicate short Pathfinder line r
docs/ui-parity.md | 1 | IME row main's, Font menu row next's

i18n/fr.rs: five rows dropped that main's shared table now has (Row, Open Path, Flex Layout, Current Page Number, Rulers).
2026-10-16 03:09:22 +00:00
aiongg 953b411342 Merge PR #501: Layout: parent pages pass margins and guides to their pages 2026-10-16 02:51:49 +00:00
aiongg df46ccf1c6 Merge PR #330: Compose: the CJK leading model and character alignment apply only under the Japanese composers 2026-10-16 02:51:48 +00:00
Marco Arnulfo 2b9aae64a4 Placed greyscale and bitmap images take the swatch applied to them, as in InDesign (IDML, screen, PDF) 2026-10-16 03:07:52 +02:00
Gert Pellin 801ab7d1f3 Table of Contents: page number and separator character styles, Sort Entries in Alphabetical Order (#1069)
Each included style gets pageNumberStyle, separatorStyle and sort.
toc.generate and toc.update apply the character styles to the page
number and the separator of every entry, and list the entries of a
sorted style alphabetically among their siblings, each with the
lower-level entries under it. Unknown character styles are errors.
Renaming or deleting a character style updates the definition. IDML
reads and writes PageNumberStyle, SeparatorStyle and Sort.
2026-10-15 20:32:20 +02:00
midasmatterhorn 46a914dc4c idml: one unusable CJK metric no longer refuses the whole file
Composite-font and mojikumi metrics were taken from the file with no
sanity check, and Document::check rejects a zero, NaN or infinite
relative_size / horizontal_scale / vertical_scale / baseline_shift — so
one attribute anywhere in the package (RelativeSize="0",
BaselineShift="NaN") failed the import with "invalid composite font
metrics" and left the file unopenable. Every neighbouring number in this
importer already filters; the metrics now do too and fall back to a
usable value.

Regression test: unusable_cjk_metrics_do_not_refuse_the_whole_file —
fails on main with Invalid("invalid composite font metrics: Rubies"),
passes with the fallback.
2026-10-15 15:42:25 +03:00
Keyne PEKARandCodex e569381c2e IDML872.1: preserve liquid guides through interchange
Keep GuideType=Liquid on document and parent pages, with literal XML and
mixed-guide regression coverage. Leave object liquid constraints separate.

Co-Authored-By: Codex <codex@openai.com>
2026-10-15 17:13:35 +08:00
wstockandClaude Fable 5.1 02bc1e38aa Importers: XML element nesting and DTD group nesting are bounded (#712)
The DOCX and IDML element-tree builders accepted any depth, and the
tree is then walked and dropped recursively; a DTD content model's
groups recursed without a budget. Each parser now refuses nesting past
a fixed budget with an error, before any recursive consumer runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011K1peyrh5MdZ6ttHkVq8Gh
2026-10-15 07:13:51 +00:00
aiongg e22205039c test(idml): the liquid-rule round trip detaches page 1's parent along with its local margins 2026-10-15 06:40:32 +00:00
aiongg d4dd4287e1 Merge upstream main a930276 into parent-margins: local_margins replaces the parent-margin equality rule
Conflicts:
- crates/doc/src/build.rs: kept parent_page_of/parent_page/parent_guides,
  sync_parent_margins and mark_differing_margins_local; inherited_guides(abs)
  stays as a wrapper over parent_guides for the guide commands and dialogs.
  Kept main's single_sided_page_shows_first_parent_page test next to
  differing_margins_from_older_files_stay_local.
- crates/doc/src/page.rs (auto-merged): Spread::parent_page_index takes main's
  rule (first parent page for left and single-sided pages).
- crates/engine/src/cmd/layout.rs: the parent block uses sync_parent_margins
  instead of main's old-margins equality check; parent accepts prefix, label or
  index; document pages set here become local. Main's
  parent_margins_change_the_parent_and_follow_unoverridden_pages stays; added
  applying_a_parent_gives_its_margins_and_local_margins_stay_local.
- crates/tools/src/snap.rs, crates/ui-egui/src/canvas.rs: guides read through
  parent_guides (no per-page Vec); main's current_spread_page and
  current_parent_page kept.
- crates/ui-egui/src/panels/properties.rs: main's parent-target margin fields.
- crates/format/src/lib.rs: upgrade and merge_duplicate_assets, then
  mark_differing_margins_local.
- crates/engine/src/tests_idml.rs: both sides' tests.
- ROADMAP.md: main's file plus this PR's progress-log line.
2026-10-15 06:35:36 +00:00
aiongg ac524c4e7a Merge upstream main a930276 into fix/idml-interchange: placed PDF crop on top of main's PDFAttribute page
Conflicts and how each was resolved:
- crates/idml/src/import.rs: main's linked_assets and insert_asset kept; the
  crop is read beside main's page (PDFAttribute as a child or a property), and
  linked_assets is keyed by (path, page, PdfCrop) so two crops of one page are
  two assets.
- crates/idml/src/export.rs: PDFAttribute with PageNumber, PDFCrop and
  TransparentBackground is written for every placed PDF, replacing main's
  page > 0 block.
- crates/idml/src/tests.rs: main's file plus the `$ID/` font tests.
- crates/engine/src/cmd/file.rs: main's exact PDF page size, place cursor and
  add_asset kept; Crop to stores the choice on the asset (the graphic spans
  the box), so the place cursor's frame crop box is gone.
- crates/engine/src/cmd/{book,interchange}.rs: main's confined and reporting
  imports, with the PDF crops resolved after them.
- crates/engine/src/cmd/print.rs: main's print_with, exporting with the
  measured PDF boxes.
- crates/engine/src/cmd/links.rs: main's text links plus pdfCrop.
- crates/engine/src/cmd/{datamerge/fill,mathexpr,media,table}.rs,
  crates/format/src/lib.rs: main's add_asset with the asset defaults.
- crates/engine/src/cmd/package.rs, crates/engine/src/tests_idml.rs: both
  sides' tests.
- crates/fonts/src/fontdb.rs: main's style traits, looked up through
  plain_family.
- crates/render/src/lib.rs: pdf_crop_box replaces pdf_page_box; main's
  UserUnit test checks pdf_crop_box.
- crates/ui-egui/src/dialogs.rs: main's dialog with the content-bounds crops.

Also: Asset::same_file compares the PDF crop, so placing or saving no longer
merges two crops of one page; Asset literals added on main take the defaults;
main's PageNumber test checks the crop and that page 1 is written; new tests
a_pdf_page_placed_with_two_crops_imports_as_two_assets and
pdf_assets_with_different_crops_stay_apart.
2026-10-15 06:34:45 +00:00
aiongg 903cc617b2 Merge upstream main a930276 into fix/cjk-leading-model-composer: per-paragraph Japanese composer gating replaces the import-time leading model drop
Conflicts:
- crates/compose/src/lib.rs: kept main's line placement (line_metrics with first
  baseline tops, baseline grid snap) and this branch's leading model and Aki Below
  advance. The Hyphenation Zone check uses Composer::single_line(), so the Japanese
  single-line composer gets the zone too.
- crates/idml/src/tests.rs: kept both tests at the conflict (composer kinds, text
  stroke alignment).

Also resolved:
- crates/idml/src/import.rs: removed the package scan for a Japanese composer and the
  drop of [No paragraph style]'s leading model; the import keeps the file's leading
  model and the composer kind, and compose applies the model per paragraph.
- crates/idml/src/tests.rs: the default-leading-model test lays out the document and
  checks Roman spacing under the non-Japanese composers and the Aki Below spacing
  under the Japanese ones; a new test round-trips a Japanese composer document.
2026-10-15 06:16:29 +00:00
Brandon Thomas 41e7a023da Merge PR #304 2026-10-15 00:35:26 -04:00
Brandon Thomas 84dbba029f Merge IDML/text cluster
# Conflicts:
#	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
2026-10-14 23:54:49 -04:00
Brandon ThomasandClaude Opus 5.5 0697b39a3a fix: integrate batched community PRs (rustfmt after the IDML/text cluster merges)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:45:50 -04:00
Brandon ThomasandClaude Opus 5.5 5112019053 fix: integrate batched community PRs (quick-xml 0.41 from #687 in main's IDML attribute reader; #610's text-link reads pass #604's file guard; wasm-only unused re-export)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:36:14 -04:00
Brandon Thomas 528264a4a9 Merge PR #823 (with main's radial ellipse: linear gradients go through #823's linear_axis) 2026-10-14 23:07:06 -04:00
Brandon Thomas bfd2c59867 Merge PR #687
# Conflicts:
#	crates/textimport/src/docx.rs
2026-10-14 22:57:24 -04:00
Brandon Thomas 131c4f417f Merge PR #972
# Conflicts:
#	crates/idml/src/export.rs
#	crates/idml/src/tests.rs
2026-10-14 22:44:44 -04:00
Brandon Thomas 999bb848cc Merge PR #963
# Conflicts:
#	crates/idml/src/import.rs
#	crates/idml/src/tests/assets.rs
2026-10-14 22:44:14 -04:00
Brandon Thomas 072ae1d948 Merge PR #975
# Conflicts:
#	crates/idml/src/import.rs
#	crates/idml/src/tests.rs
2026-10-14 22:43:41 -04:00
Brandon Thomas d5cea4f3de Merge PR #971
# Conflicts:
#	crates/idml/src/import.rs
#	crates/idml/src/tests.rs
2026-10-14 22:42:51 -04:00
Brandon Thomas ce222cd8a6 Merge PR #921
# Conflicts:
#	crates/idml/src/import.rs
2026-10-14 22:38:53 -04:00
Brandon Thomas 8cbef21444 Merge PR #974 2026-10-14 22:05:13 -04:00
Brandon Thomas 66531c8b63 Merge PR #952 2026-10-14 22:05:13 -04:00
Brandon Thomas 3adc568fc3 Merge PR #799 2026-10-14 22:05:11 -04:00
Brandon Thomas 3cdda36680 Merge PR #657 2026-10-14 22:05:11 -04:00
Brandon Thomas 0e63dcad11 Merge PR #604 2026-10-14 22:05:10 -04:00
sanyamsingh001andCodex e35c430628 Preserve stored IDML page liquid rules
Co-Authored-By: Codex <codex@openai.com>
2026-10-15 09:30:58 +10:00
NetterSilverandClaude Haiku 5.5 4626c4b084 Bound decompressed size and entry count of native and IDML ZIP imports
Fixes #710

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 01:24:38 +02:00
moppeeerandClaude Opus 5.5 099fbd23ee IDML: import and export ruler guides
Guides in an IDML page (<Guide> children of <Page>) were dropped on import,
and DesignCraft's own guides weren't written. Montilla Design's Flex Layout
file keeps its 16 column guides on the B-Parent page; none came in.

Import: Location is measured from View Preferences › Ruler Origin (the
spread's top left, the page's, or the spine); Orientation, FitToPage (off:
a spread guide), Locked and ItemLayer are kept. Export writes each page's
guides under its <Page> in spread-origin coordinates, which is the ruler
origin DesignCraft writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:54:34 +02:00
moppeeerandClaude Opus 5.5 85d7c16161 IDML: InDesign's default CJK leading model only with a Japanese composer
InDesign writes LeadingModel="LeadingModelAkiBelow" on [No paragraph
style] of every document, Western ones included, but only its Japanese
composers use the leading model. DesignCraft applied it to every paragraph,
so a 24 pt paragraph after a 32 pt one sat 34.53 pt below it instead of the
Roman 32.8 (4 pt space after + 28.8 auto leading) in Montilla Design's
Flex Layout exercise file, and its fitted captions came out overset.

The import now drops that default when no part of the package applies a
Japanese composer (HL Composer J / HL Single J). Leading models set on
text, other styles, or in documents that use a Japanese composer are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:49:33 +02:00
midasmatterhorn 8cfaa90dcb idml: read PDFAttribute PageNumber so placed PDFs show their page
A placement of a multi-page PDF carried no page number through IDML: graphic()
always made the asset with page 0 and keyed linked_assets by path alone, so
every placement of one file showed page 1 (InDesign's PDFAttribute PageNumber
was ignored). Import now reads PDFAttribute/@PageNumber (1-based) into
Asset.page, linked_assets is keyed by (path, page), and export writes
PDFAttribute back for pages past the first.

Regression test: the same file placed at pages 2, 1 and 2 imports as two
assets with pages 1 and 0 and round-trips through export.
2026-10-14 23:46:19 +03:00
NetterSilverandClaude Haiku 5.5 2302b7ec7f Keep viewBox-only placed SVGs at viewBox size on IDML import
Fixes #950

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 19:42:32 +00:00