Commit Graph
44 Commits
Author SHA1 Message Date
aiongg 7979a417fe Integration: cargo fmt 2026-10-16 04:21:41 +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 1a6ee2ca70 Integration: compile fixes after merging main a184db6f8
- compose: a test frame literal named section_marker twice.
- export: PPTX rasterizing keeps a wrap stand-in for each rasterized object (next's rasterize_where takes keep_wrap).
- flex: to_bytes returns a Result on main.
- package, combined_close_glyph test: load_document_fonts takes an access check.
- pptx test: Graphic literal gains next's clip and wrap fields.
- render placed_pdf test: PdfCrop imported twice.
2026-10-16 03:11:36 +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 2f2f9d6461 Merge PR #362: IDML: $ID/ font names, placed PDF page and crop, package IDML without embedded images 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
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 21f14d7f79 fix(render): wide feathers and inner effects stay sharp at high zoom 2026-10-15 06:15:21 +00:00
Brandon Thomas 4bd1fbab37 Merge PR #765 2026-10-14 22:05:11 -04:00
Brandon Thomas 63c3e36644 Merge PR #698 2026-10-14 22:05:11 -04:00
Brandon Thomas 3cdda36680 Merge PR #657 2026-10-14 22:05:11 -04:00
Brandon Thomas 7a9570b56e Merge PR #570 2026-10-14 22:05:10 -04:00
moppeeer 52e0aaa2d6 Merge remote-tracking branch 'origin/main' into codex/radial-gradient-ellipse
# Conflicts:
#	crates/engine/src/cmd/style.rs
2026-10-14 10:32:47 +02:00
moppeeer ed6bcae877 Merge remote-tracking branch 'origin/main' into codex/gradient-cmyk-blend 2026-10-14 09:12:51 +02:00
moppeeer 6f3f32a445 Merge remote-tracking branch 'origin/main' into codex/effects-on-pasted-into 2026-10-14 09:10:42 +02:00
aiongg 063856cc4c Merge t14-b: #836 #840 #846 #854, Find/Change first:true
# Conflicts:
#	ROADMAP.md
2026-10-14 00:09:13 +00:00
aiongg 71309b993e Integration: cargo fmt 2026-10-13 22:40:00 +00:00
aiongg b9cbfca8cf Merge #843: Placed PDFs: render with pdfcraft's patched hayro crates
# Conflicts:
#	Cargo.toml
#	ROADMAP.md
#	crates/render/src/lib.rs
2026-10-13 22:38:19 +00:00
aiongg dc2dda7717 Merge #836: gradients between process colours blend as inks
# Conflicts:
#	ROADMAP.md
2026-10-13 22:31:16 +00:00
aiongg 7f0d7f1f32 Merge t13-moppeeer: #253 #832 #753 #754 (a3b97165) #588
# Conflicts:
#	ROADMAP.md
#	crates/idml/src/tests.rs
2026-10-13 19:57:37 +00:00
dotCooCoo 7a89954937 Placed PDFs: render with pdfcraft's patched hayro crates
Placed PDFs rendered through hayro 0.7 from crates.io, whose
hayro-interpret divides integers in calculator functions without a
check, does not limit how deep functions nest and samples each mesh
triangle over its whole bounding box. A placed PDF whose calculator
function runs `1 0 idiv` or `-2147483648 -1 idiv` panicked the render.
A chain of 100,000 functions, each stitching function naming the next,
overflowed the stack and aborted the process. Mesh shadings with points
far off the page, ten thousand overlapping triangles or forty thousand
patches ran for over a minute or passed 2 GiB of memory.

[patch.crates-io] now takes hayro, hayro-interpret, hayro-syntax and
hayro-jbig2 from pdfcraft's vendor/ directory at b5467d93, with the
patches its vendor/README.md lists. The crate versions do not change.
The patched hayro-syntax sizes a page with a /UserUnit in points, and
pdf_page_box scales the page's boxes by the same unit, so with Crop to
the frame still shows the chosen box. NOTICE names the new source and
the licenses. The function and mesh tests in
crates/render/tests/placed_pdf.rs follow pdfcraft's tests in
crates/render/src/raster.rs.
2026-10-13 12:52:10 -07:00
moppeeer 58b9950fb6 feat(effects): Inner Glow mode and technique; Basic Feather choke and corners
InDesign's Effects dialog sets an Inner Glow's blend Mode (Screen by
default) and Technique (Softer or Precise), and a Basic Feather's Choke
and Corners (Sharp, Rounded, Diffused). LYH Studio episode 14 shows them
at 12:05–12:20 (Screen 75 %, Softer, 0.25 in; 0.0625 in, Diffused).

- doc: InnerGlow.mode and .technique (GlowTechnique), Effects.
  feather_options (FeatherOptions: choke %, FeatherCorners), with
  InDesign's defaults; old documents read as before.
- render: the glow uses its mode; Precise keeps it nearer the edge; the
  feather's choke shortens the fade, Sharp and Rounded corners follow
  the shape more tightly or softly than Diffused (unchanged, so the
  goldens stay).
- render fix: an inner glow's (and a bevel's) blend mode was applied
  inside the clip layer, against its empty backdrop, so Screen and
  Multiply acted as Normal; the blend layer now wraps the finished edge
  and mixes with the object below.
- engine: choke clamped to 0–100; the params documented.
- Effects dialog: Mode and Technique on Inner Glow; Choke and Corners on
  Basic Feather; strings translated.

Builds on #643 (the Effects dialog).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a3b971654f)
2026-10-13 19:33:40 +00:00
aiongg 9d5d341a7d Merge #765: Radial gradients stretch with the object's box (an ellipse) 2026-10-13 19:21:58 +00:00
moppeeerandClaude Opus 5.5 dc712baeca fix(color): gradients between process colours blend as inks, as InDesign shows them
LYH Studio episode 6 builds every gradient from C=0 M=100 Y=0 K=0 to
C=100 M=0 Y=0 K=0. InDesign blends a gradient's process stops as inks:
halfway is C 50 M 50, a light lilac. DesignCraft converted both ends to
RGB and blended there, so the middle came out a darker violet (117, 88,
189 instead of 135, 119, 181), and every Episode 6 gradient looked
different. A midpoint between CMYK stops also became an RGB colour, so
the PDF shading turned RGB with a conversion warning.

- color: `Color::lerp` blends CMYK with CMYK (or grey), and grey with
  grey, in ink space; anything with RGB still blends in RGB.
- `Gradient::display_stops`: the expanded stops plus samples through
  each span that isn't RGB at both ends, for renderers that interpolate
  in RGB; the screen renderer uses it.
- pdf: a midpoint's expanded stop now keeps the stops' space; the
  colour-space test expects no conversion for CMYK or grey midpoints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:38:38 +02:00
moppeeerandClaude Opus 5.5 7a30e5427e fix(render): radial gradients stretch with the object's box, as in InDesign
InDesign draws a radial gradient in the object's bounding box: on a
non-square frame it's an ellipse reaching the frame's sides, and a
Gradient Swatch tool drag keeps those proportions. LYH Studio episode 6
(3:16) gives the left page's background a radial gradient: magenta in
the middle, cyan out to every edge. DesignCraft drew a circle reaching
only the longer side, so a portrait page stayed purple at its sides.

- doc: `Fill::radial_ellipse(bounds)`: centre and radii (default: the
  box's centre and half its width and height; with a tool vector, its
  start, and its length measured with the box made square).
- render: the radial paint is that circle, stretched by the paint
  transform; PDF: the shading's transform does the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:58:05 +02:00
aiongg 578818466e Merge craft-cms: Colour management on craft-cms 2026-10-13 13:08:18 +00:00
aiongg 18991a8fc2 Colour: effects goldens with Coated FOGRA39 swatch colours 2026-10-13 13:00:24 +00:00
aiongg bcad0182e5 Merge pr/698: Effects apply to frames with pasted-in content (Inner Glow, Basic Feather, shadows…) (#698) 2026-10-13 12:27:17 +00:00
moppeeerandClaude Opus 5.5 96e9f371ac fix(render): effects apply to frames with pasted-in content
A frame holding items pasted into it (Edit › Paste Into) was drawn by
its own branch in draw_item that skipped every effect: Basic Feather,
Inner Glow, shadows and the rest were stored but never drawn. LYH Studio
episode 14 (11:51) gives the outlined "GET MORE." title, with a photo
pasted into it, an Inner Glow and a Basic Feather.

Such a frame now goes the way of any other object: draw_body draws its
fill, its items clipped to the frame and its stroke, so draw_item_fx
wraps all of it. Its silhouette counts as solid for shadow spread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:02:57 +02:00
moppeeerandClaude Opus 5.5 6f47f10b4b fix(render): Blend modes don't blend with the paper
The renderer painted the paper, then blended page items with it: black
text in Color Dodge over blank paper dodged the white to white and
vanished, and other modes showed the same way. In InDesign (and in PDF,
where the page group starts transparent) the paper is not a backdrop:
over blank paper an object in any blend mode shows as it is, and only
the objects below it are blended with. On spreads that use a blend mode
the items are now drawn in their own group, laid over the paper once;
other spreads render as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:33:13 +02:00
aiongg 71db506182 Merge pr/643: UI: the Effects dialog, as in InDesign (#643)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/object.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/uk.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-13 00:12:33 +00:00
aiongg e391002be6 Merge text-stroke-behind: Text: stroke behind fill (#619)
Conflict: crates/render/src/text.rs: the branch's paint_glyphs with next's fill and stroke overprint (overprint_fill / overprint_stroke, an overprinting() Multiply layer around each paint); next's cell_stroke(kind, weight, length); path text strokes don't overprint, as before
Conflict: crates/pdf/src/text.rs: the branch's text_run / paint_outlines / glyph_outlines split with next's skew tan, vertical mark offsets and split_clusters glyph order (#421)
Conflict: crates/engine/src/cmd/paths.rs: next's shatai transform + the branch's stroke_behind_fill key
Conflict: crates/idml/src/tests.rs: tests both
Conflict: docs/typography-parity.md, ROADMAP.md: headers combined, logs both
2026-10-12 22:04:10 +00:00
moppeeerandClaude Opus 5.5 4769cadb77 feat(ui): The Effects dialog, as in InDesign
Object > Effects (Transparency..., Drop Shadow... and the rest, and their
shortcuts) and the Effects panel's fx button open the Effects dialog on
the chosen effect, turned on: the effect list with checkboxes on the left
and a summary line, the chosen page's settings on the right in the
document's units (Blending, Position with X/Y offsets, Options...),
Preview on the page while it is open, Cancel putting the page back, and
OK as one undo step.

- engine: object.effects {effects (merged, kept in range), opacity, blend}
- doc: drop and inner shadows default to InDesign's 120 degree light
- ui: dialogs::cancel (a previewing dialog reverts)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:14:15 +02:00
aiongg 270dd46b50 Text: stroke behind fill (#619)
A character attribute strokeBehindFill paints a text stroke under the fill
at double width, so its full weight lies outside the letters and they keep
their shape. Screen and PNG rendering, PDF export (stroke as outlines
first, then the filled text, so the text is not duplicated; clipped to the
outside of the glyphs when there is no fill), Create Outlines (outside-
aligned stroke), the Properties panel and Character Style Options.

IDML StrokeAlignment on CharacterStyleRange, CharacterStyle and
ParagraphStyle imports and exports as the attribute (OutsideAlignment =
on, CenterAlignment = off).
2026-10-12 21:06:42 +00:00
aiongg dcabd61597 Merge #570: Blend modes don't blend with the paper
Conflict: crates/render/src/lib.rs: push_layer after next's back document-grid drawing and pop_layer before the front grid, so items never blend with the grid
Conflict: ROADMAP.md: kept both progress-log lines
2026-10-12 12:24:20 +00:00
moppeeerandClaude Opus 5.5 6cc6f92dad fix(render): Blend modes don't blend with the paper
The renderer painted the paper, then blended page items with it: black
text in Color Dodge over blank paper dodged the white to white and
vanished, and other modes showed the same way. In InDesign (and in PDF,
where the page group starts transparent) the paper is not a backdrop:
over blank paper an object in any blend mode shows as it is, and only
the objects below it are blended with. On spreads that use a blend mode
the items are now drawn in their own group, laid over the paper once;
other spreads render as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:33:52 +02:00
aiongg 9f0b34e876 Merge #177: Memory: release retired layouts and reserve text growth exactly
Brings #175 (abf9a40, PSD fixed-size chunks).
Conflict: crates/engine/src/cmd/text.rs: tests only; kept #118's story_query_contract_tests and #177's reserve_tests (insert()'s code merged cleanly: #177's reservations around #84's typing format)
2026-10-09 22:53:16 +00:00
skylar taylor-barrick 4e4eaad1c8 PERF-MEM: release retired layouts and trim edit-history capacity 2026-10-08 15:35:20 -06:00
Brandon ThomasandClaude Opus 5.5 0237d5de93 Merge origin/main into fix/compose-baseline-frame-fit
Vertical Type is now the story's direction: the full-descent check reads
FrameSpec::vertical, and the vertical-extent test sets the story vertical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:20:01 -04:00
skylar taylor-barrick 8d56e875eb Compose: fit horizontal text frames by the last baseline 2026-10-07 16:10:43 -06:00
skylar taylor-barrick 4910e42bcc Render: keep outside-aligned strokes out of closed interiors 2026-10-07 16:10:24 -06:00
Brandon ThomasandClaude Opus 5.5 8490965e71 CI: fix new Rust 1.99 clippy lints (as_chunks, ?, redundant ref)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:37:09 -04:00
Brandon ThomasandClaude Opus 5.5 b13135472a V: Rotate Spread per spread — canvas layout through per-spread transforms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:25:16 -04:00
Brandon ThomasandClaude Opus 5.5 b62a795602 M9: render perf (glyph path cache, culling) + soft drop shadow/feather/glow; perf harness
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:31:01 -04:00