Commit Graph
259 Commits
Author SHA1 Message Date
bflatastic ca0785573d Add-ons: Arrange Tools — Arrange in Grid, Swap, Replace with Top Object, Set Size of Each, Zoom to Selection
A built-in add-on in Object › Plug-ins › Arrange Tools (#4830, #4831, #4832, #4833, #4834):

- addon.arrange.grid lays the top-level selected objects out in rows and
  columns, a row or a column (stacking order from the back, visual bounds,
  each object centred in its cell), from the selection's top-left or
  centred on the artboard.
- addon.arrange.swap swaps two objects' bounds centres, and with `size`
  their widths and heights.
- addon.arrange.replace puts a copy of the topmost selected object centred
  on each of the others, in its stacking place, kept, stretched or fitted
  in proportion; the replaced objects are deleted unless `keep`.
- addon.arrange.size gives each selected object an exact width and/or
  height about a reference point.
- addon.arrange.zoomSelection (a UI command) fits the active view to the
  selection.

Places and sizes are measured as object.setBounds measures them (its
transform, now also available per object), strokes scale as Scale Strokes
& Effects says, each command is one undo step, and Arrange in Grid,
Replace with Top Object and Set Size of Each open previewed dialogs.
Catalog rows, command sweep cases, docs/mcp.md, parity docs and the
ROADMAP progress log.
2026-10-31 22:22:15 +07:00
bflatastic 54b0c7266c Fix #4826: Artwork Cleanup's Join Point Text stacks one-line type into one object 2026-10-31 20:44:15 +07:00
bflatastic 79f7b393e9 Add-ons: Path Precision Tools — Shared Edge Merge
Path Precision Tools gains the Shared Edge Merge Tool, kept out of the
toolbars (All Tools drawer and Object › Plug-ins › Path Precision Tools,
no shortcut), and Keep Shared Points Together:

- Shared Edge Merge Tool (sharedEdge): over two paths touching at two
  coincident anchors (tolerance 0 to 3 pt) hovering shows the two
  stretches and the merged edge; a click replaces both with it, half way
  between them, or following the upper (Shift) or lower (Alt) path. Both
  paths stay closed or open, anchors either stretch has inside are
  matched on the other, one undo step.
- Making ends: near anchors meet on a click and an anchor one path lacks
  is added to match; Shift+Alt asks for this inside a stretch.
- Keep Shared Points Together: an option that makes Slide Anchor Along
  Path, Extend Path and Set Anchor take coincident anchors along. Nothing
  else in the app changes.
- Shared Edges section in the Anchor Inspector: brush diameter,
  coincidence and the option.
- Commands addon.pathtools.mergeEdge and movePointsTogether, params
  validated and covered by the command sweep.
- Cursor stand-in, catalog rows in every language, docs and tests.
2026-10-31 07:16:22 +07:00
bflatastic e13c4bc9d0 Add-ons: Path Precision Tools — live path effects 2026-10-31 06:10:47 +07:00
bflatastic c9dc4fa857 Add-ons: Path Precision Tools — Corner Tool and Slide Anchor
Path Precision Tools gains two tools, kept out of the toolbars (All Tools
drawer and Object › Plug-ins › Path Precision Tools, no shortcuts):

- Corner Tool (cornerTool): editable corners on any path through the
  existing Live Corners. Hover shows an eligible anchor, a click cuts a
  corner with the tool's radius and kind, a drag sizes it (Shift: whole
  values), a corner's marker selects it and loads its values, resizes it
  and removes it at 0 or on a double-click; a corner never grows past
  what fits; an Alt-marquee cuts every eligible anchor inside.
- Corners section in the Anchor Inspector: radius, kind, selected count,
  Apply to Selected Anchors.
- Slide Anchor Along Path (slideAnchor): a drag runs an anchor along its
  curve, the neighbouring segments refitted by the keep-shape kernel;
  Shift snaps where the tangent is a multiple of 45° from the constrain
  angle.
- Commands addon.pathtools.cornerSet, cornerRemove and slideAnchor, one
  undo step each, params validated and covered by the command sweep.
- Cursor stand-ins, catalog rows in every language, docs and tests.
2026-10-31 04:21:26 +07:00
bflatastic e556dacbb6 Add-ons: Artwork Cleanup — text tools
Object › Plug-ins › Artwork Cleanup and a hideable Text row in its panel
gain text tools, each one undo step:
- Break Text into Lines / Paragraphs / Words / Glyphs: point type pieces
  placed by the glyph of their first character, keeping their styles.
- Combine Point Text: one-line point type merged into as few objects as
  it can, every glyph staying where it was (tracking and spaces, checked
  against the layout).
- Split Text at Cursor, Remove Line Breaks, Remove Paragraph Breaks,
  Set Alignment, Keep Position, Reset Text Transform and Replace
  Missing Fonts.
2026-10-31 02:05:58 +07:00
bflatastic ebc5b7038c Add-ons: Color Effects — Duotone effect and Print Ink Fixes
Effect › Color Effects › Duotone… (addon.color.duotone, also in Object ›
Plug-ins › Color Effects) prints the grey-equivalent tone of each colour
with one to four inks, each with its own tone curve. It is a colour
adjustment like the add-on's others, so it stacks with them and works as
a filter through addon.color.filter.

- One tone of a swatch the document has gives a tint of that swatch,
  linked, so a spot tone separates onto its plate; several tones give
  the CMYK mix of their tints (the inks overprint, so they multiply).
- Colour adjustments now map paints with their swatch links
  (ColorMap::apply_link); the existing adjustments behave as before.
- Skip Objects with Spot Colors leaves spot-linked colours as they are.
- The dialog has a row per tone and the picked tone's curve (the Curves
  graph, reused), its points and Reset to Linear / Inverse.

Print Ink Fixes (addon.color.printFixes, Object › Plug-ins › Color
Effects › Print Ink Fixes…) scales CMYK colours over a total ink limit
down to it, removes white overprint and sets black overprint (Overprint
Black's logic) in a CMYK document, as one undo step, and reports what
changed. RGB documents are refused.

The panel opens both under Inks.

Deferred: an opaque undercoat, tones for type only, extra gradient stops
for smoother tints, curve flip and all-100 % buttons.
2026-10-31 00:26:18 +07:00
bflatastic f4c245286e Add-ons: Color Effects — live Vector Halftone effect
Effect › Color Effects › Vector Halftone… (addon.color.halftone, also in
Object › Plug-ins › Color Effects) draws its object as the halftone dots
Object › Vector Halftone makes, live: mono (one dot colour, or each dot in
the art's colour under it) or CMYK screens, the five dot shapes,
frequency, angle, invert and clip to the art's outline.

- The screens, dots and caps move to vectorcraft_effects::addons::halftone,
  shared by the command (its behaviour and tests unchanged) and the effect.
- The renderer samples the art (vectorcraft_render::addons::halftone) and
  keeps the dots by the object's content and what it reads from the
  document, so unchanged art isn't sampled again; past 100000 cells per
  screen the object draws as it is.
- Expand Appearance turns the object into its dots, and the SVG, PDF, EPS
  and AI exporters write them as paths.
- Its dialog reuses the command's fields, without Keep Original.

Deferred: stochastic and radial patterns, a dot-gain curve, per-screen
separation settings, type or symbols as dots.
2026-10-30 22:13:33 +07:00
bflatastic 27ed249b1f Add-ons: Parametric Shape Tool — re-editable stars, polygons, rings, arrows, gears and more
Path Precision Tools gains the Parametric Shape Tool: ten kinds of shape
(rectangle, ellipse, polygon, star, ring, arrow, cross, heart, gear,
speech bubble) that stay editable as numbers.

- Geometry in vectorcraft_pathops::addons::pathtools::shapes: each kind's
  outline, corner rounding limited to the middle of a side, a fingerprint
  of the outline, recovery of centre, size and rotation after a move,
  turn or scale, and recognition of plain rectangles, ellipses and
  regular polygons.
- The tool (parametricShape, All Tools drawer and Object › Plug-ins ›
  Path Precision Tools only, no shortcut): drag from the centre (Shift:
  45° steps), arrow keys change the count and values while dragging,
  handles edit a selected shape, double-click places one.
- Commands addon.pathtools.shape.create / .set / .convert, one undo step
  each; params checked (counts 3..1000, sizes up to 16384 pt).
- A shape keeps its numbers in one object-data entry (vc-pathtools-shape)
  that SVG export already leaves out; anchor edits make it a plain path.
- Parametric Shape panel, cursor stand-in, catalog rows in all 25
  languages, docs and tests.
2026-10-30 20:55:14 +07:00
Dan Green 7688b41a40 Libraries panel: art and colours dragged in are added where they are dropped
Art dragged off the canvas and colours dragged in from elsewhere (swatches, another library's)
used to be added at the end of the ungrouped items, wherever they were dropped. They now land
as items dragged within the list do: on another item of their kind, just before or after it by
the pointer's half and in its group; on a group, into it; away from the items, ungrouped at the
end as before.

library.add takes group and before/after, placed as library.moveItem places an item (the
placement is shared). A library change that fails partway now leaves the library as it was.
2026-10-30 20:20:17 +07:00
bflatastic 1ca3db1b51 Add-ons: Smart Pen — draw and edit paths with one tool, panel
A built-in add-on under Object › Plug-ins › Smart Pen:
- The Smart Pen Tool draws and edits Bézier paths without switching
  tools: a click adds the next segment straight or curved (a panel
  toggle; Tab flips the segment being placed or the last one), a drag
  makes a smooth point (Alt breaks the handle, Shift snaps to 45°, Space
  moves the point), a curve after a straight segment joins it tangent.
- On existing paths it selects and moves points, removes a point keeping
  the shape (Alt-click), toggles smooth/corner (double-click), drags
  handles, reshapes a segment by dragging it and inserts a point on it
  (Alt-click); it closes, continues and joins open paths.
- The Smart Pen panel: next segment straight or curved, remove points
  keeping shape, make smooth, make corner, retract handles, close or
  join.

The tool stays out of both toolbars as shipped; the All Tools drawer
lists it under the add-on's heading. Commands are addon.pen.*; the
existing Pen tool, commands and menus are unchanged.
2026-10-30 16:02:27 +07:00
bflatastic beb0ae6515 SVG export: rectangles, circles, ellipses, lines and straight paths as SVG shape elements (#4652) 2026-10-30 09:45:59 +07:00
Taha-Mahmoodi 887328db03 Fix #4420: import attached recolor adjustment layers as color overlays
In Affinity Designer, Recolor Adjustment layers (RcRA) can be attached to
vector groups/shapes via AdCh. Lightness 1.0 recolors target elements to
white. Import visible RcRA layers as ColorOverlay effects and apply them
during node appearance evaluation.
2026-10-30 00:15:58 +07:00
dotCooCoo f32699bea7 File formats: PCX (.pcx) files open and place
A new reader, written from the ZSoft PCX Technical Reference Manual, decodes
the run-length encoded scan lines of 1-bit images in the header palette's
first two colors (black and white when the version has no palette or the two
colors are the same), 1-bit images in two to four planes and 4-bit images
with the header's palette (the EGA colors for versions 0 and 3), 2-bit CGA
images in the colors that the header's CGA background and palette settings
pick, 8-bit images with the 256-color palette after the image data (gray
levels without one) and 24-bit images in three planes. The image is stored as PNG at the resolution of the header's HDpi and
VDpi fields.

Open and Place list PCX, and pasting a copied .pcx file and Glass's Load
Texture… read it by its header. A file of another depth or plane count, one
cut short and one whose pixels as RGBA pass the pixel allowance are refused
with a message before its pixels are decoded. The desktop packaging does not
associate .pcx files, and no export writes them.

Docs: docs/file-format-parity.md, docs/target-app-parity.md, docs/mcp.md,
docs/releasing.md.
2026-10-30 00:15:56 +07:00
VoiceLessQ 401706ff8b PDF import: each Contents object is read once when naming a refused stream
Pages that share one Contents array made the name of a refused stream read that array once per page, quadratic in a crafted file. Each object is now read once, and a page's own array as before. From the review of #4612.
2026-10-30 00:15:54 +07:00
VoiceLessQ f0865193a8 PDF import: a page's content listed in an array object is named as content
A page's Contents can be an array written as an object of its own (/Contents 6 0 R). Its streams were named "a stream in it": the reference was compared with the stream, not the array's items. From the review of #4603.
2026-10-29 23:39:24 +07:00
VoiceLessQ b96d35139c PDF import: a refused file says which stream it trips on (refs #4366)
Squashed from #4603 onto main.
2026-10-29 22:12:16 +07:00
bflatastic a7125f9e9f PDF import: bound the objects an import makes and the form content a page walks (#3569)
A page drawing one form over and over made its art again for every drawing
with no limit (a 1 KB file took 877 MB), and a form decoding to 64 MiB drawn
a thousand times hung. The import now makes at most half the objects a saved
document may hold (2M, 256K on the web), leaving the rest out with a warning,
and the scan reads at most 2 GiB of form content per page before refusing it
as past the reader's limits.
2026-10-29 21:48:36 +07:00
dotCooCoo cdf4453051 Libraries: gradients and patterns can be added to a library and applied from it
Squashed from #4442 onto main.
2026-10-29 19:21:33 +07:00
dotCooCoo 6fd042e4be Tests: the MCP concurrency, folder search and artboard export tests pass on a loaded machine
Squashed from #4473 onto main.
2026-10-29 18:50:24 +07:00
Don Omondi 5b18da25a8 fix(format): remove what a native file has beyond the canvas (#3575)
Edits refuse to leave anything beyond MAX_COORD (doc_sane checks the
artboards, the guides, the ruler origin and the objects an edit touches),
but the native loader didn't check them. A file with one object, artboard,
guide or ruler origin out there opened without a warning, and then every
move failed with "result would exceed the canvas", in-range objects
included; saving kept it that way.

Document::drop_beyond_canvas removes objects whose geometric bounds aren't
finite or reach past MAX_COORD (a layer or non-clipping group keeps its
children within it), artboards with their artboard guides, and guides out
there, and puts the ruler origin back at the origin. load_file runs it
after the depth limit and reports what it did with the other load notes; a
file whose artboards all lie beyond the canvas is refused.
2026-10-29 11:07:40 +07:00
Dylan M. Taylor 7043ef6f3d Extract geometry command helpers 2026-10-28 16:44:11 +00:00
ham9zah 9a05383f34 Import: live blend check follow-ups
Review follow-ups to the live blend import (#4127):

- The check's grid saturates its neighbours' keys: a step far out (a
  coordinate like 1e300) saturates its cell, and the cells round it
  overflowed (a panic in debug builds, a wrong cell in release).
- Each laid object matches one drawn step: two drawn steps near one laid
  object no longer both take it, so a drawing the blend doesn't lay out
  isn't taken for it (the drawn steps stay).
- A blend's record is read to the length its XP gives: hex digits after
  it on the last line no longer spoil it.

Tests: a laid object matches one drawn step, 1e300 coordinates in the
check and in the import fuzz property, and a record line longer than
the record.
2026-10-26 15:11:45 +07:00
ham9zah 4214d4c9d3 Import: live blends are checked in bounded time and keep no stray art
Review follow-ups for the live blend import:

- The check of a blend against its drawn steps matched every drawn step
  against every laid one and measured centres inside the loop, so a
  crafted blend (64 keys, 1,000 steps between each) could stall the
  import. Centres and fills are now measured once, and each drawn step
  finds its neighbours through a grid of cells the tolerance wide.
- A blend lays out at most 5,000 objects to be made live, and all the
  blends of one import at most 50,000 (each laid out twice, once per
  orientation); past either, the drawn steps stay, with the note. The
  live steps take the place of drawn steps already counted against the
  reader's object limit.
- A fill the check can't compare (a gradient, a pattern) or a missing
  one no longer passes: the fills must both be none or solid colours
  within the tolerance.
- When what follows the blend's own art isn't a group of drawn steps,
  that art (its keys and spine) goes too, with the note, instead of
  staying drawn.
- The spine test can no longer pass without asserting: a spine written
  backwards keeps the keys' order (live), and a curved spine is read
  exactly while its straight-drawn steps stay drawn. New tests for steps
  with a gradient, for the blend's art when its steps aren't a group,
  and for the budget; import_fuzz gains a mutated-blend property and a
  large-blend test (998 steps live, 1,500 kept drawn, both quickly).
2026-10-26 14:10:37 +07:00
ham9zah f8bcf69b23 Import: Illustrator blends come in as live blends
A blend in a .ai file's editing data came in as plain art twice over:
the blend's own art (its key objects and spine, on `%_` lines) and its
drawn steps were both kept visible, so the keys lay doubled under the
drawn ones and the spine showed as a stray path, and nothing was live.
The spine itself was read exactly (its line and curve segments match).

The `(Adobe Path Blends) 1 0 n XP` record is now read (n bytes of hex on
the `%` lines after it, capped, checked: `DCBA`, version 1, the key
count and a record per key starting with its id). Each object's
dictionary names its part (`ShapeBlendPathType` K or S) and a key's id
(`ShapeBlendPathID`); the keys the record lists and the spine become one
live blend, the keys sitting on the spine's anchors. The record's other
fields aren't known, so the spacing is the drawn steps between two keys
(Specified Steps) and the orientation whichever lays the blend out as
drawn; the blend replaces the drawn steps only when it comes out as
drawn (each step within 0.5 pt and 0.02 of its colour). Otherwise the
drawn steps stay, without the blend's own art over them, with a note.
Behaviour as documented: https://helpx.adobe.com/illustrator/using/blending-objects.html

Save As .ai writes the PDF-compatible part only (no editing data), so a
blend is written back as its steps drawn, not live.

Hand-written tests: two keys and a straight spine as one live blend with
three steps and interpolated colours; a curved spine (l, c, L) in both
directions keeping its points and the keys' order; a garbled or short
record, a key without an id and ids the record doesn't list keeping the
drawn steps with a note; moving a key moving the steps; record parsing
on hostile input. The import fuzz target gains the record and the new
mesh keys.
2026-10-26 14:10:37 +07:00
Dylan M. Taylor 46ece73197 lint: method references and elided const lifetimes (S1612, S8863) (#4204)
Use function paths where a closure only forwarded its argument
(PoisonError::into_inner, OsStr::to_str, FindRange::is_empty), and drop
redundant 'static on associated and module consts. No behaviour change.
2026-10-25 22:08:44 -04:00
NetterSilverandClaude Haiku 5.5 6259448c39 Reset stroke weight to zero when the stroke colour is removed
Fixes #4109

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 08:18:32 +07:00
Marco Arnulfo 83600d3335 Color management: loading the working RGB profile again under its name takes effect at once 2026-10-24 19:36:34 +02:00
Patrick LenzenandClaude Opus 5.5 504b549a78 Fix #3621: bound function-based shading reads per import
Review follow-up. A function-based shading is read once per import (kept
per shading pattern and inks), and all of them draw on one budget of
200,000 function evaluations; past it a shading comes in as its average
colour, then flat grey, each with a warning that names the cause. A
gradient read leaves enough of the budget to average another shading.

A colour that changes in step with the sampling grid is checked across
the middle of the domain before an axis is chosen, so it isn't read
along the wrong one. The grey fallback warning for other shadings names
only them.

Tests: the same shading on 2,000 paths is read once; 40 distinct
shadings stay within the budget; a hostile file (5,000 fills, 20
shadings with 500-step functions) opens in 0.8 s, 253 s before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:27:44 +07:00
Fabian GrajalesandClaude Opus 5.5 1adbf259f7 Open .ai files with their swatches: the palette, its colour groups and unused spot colours
The editing data's palette (%AI5_BeginPalette) was skipped, so a .ai file
opened with the default swatches plus only the spot colours its art used:
inks the art doesn't use (a Pantone set kept in the file) and colour groups
were lost. The palette is now read in order (process, spot in CMYK, RGB or
Lab, and global colours, gradients and patterns, colour groups; versioned
content read once) and replaces the default swatches.

Xs/Xz (Registration at a tint, as printer's marks use it) are read as the
Registration swatch: before, art painted with them stopped the editing data
from being read at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 17:46:20 +07:00
vectezaandClaude Opus 5.5 1a6bc267d5 Dimension tool: Linear picks show and snap to Smart Guides
Picking the two points of a Linear dimension snapped to anchors and to the
line under the pointer, but showed no guides and ignored the art around it.
The picks now go through the drawing tools' Smart Guides (DrawSnap), hovering
too, so the guides show before each click: a point slides into line with other
objects' anchors and centres, and the second onto the construction guides
through the first. An anchor of the line under the pointer still wins (it
links the dimension to that path), then a guide, then the closest point of the
line (#2432). The guides go once the dimension is being placed.

Test: linear_picks_show_and_snap_to_smart_guides (fails on the old tool, which
showed no overlays while hovering). docs/dimensions.md updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 17:46:19 +07:00
aiongg 8cc69ed45c CI: one test binary per crate, nextest, line tables only (#3855)
* CI: line tables only for workspace crates, no debug info for dependencies

* CI: one integration-test binary per crate

Each crate's tests/*.rs files become modules of tests/it/main.rs. Files that change
process-wide state (Color Settings, the font database, installed plug-ins, the working
directory), the fuzz sweeps and the test targets the Affinity corpus workflow names stay
their own binaries.

* Docs: name the integration tests' new paths

* CI: xtask test with nextest and doctests, one wasm check, clippy --keep-going

cargo xtask ci runs the tests with cargo nextest (cargo test when it is not installed),
builds them with --lib --bins --tests, runs the doctests as their own step, and checks
the wasm crates in one cargo check --keep-going call.
2026-10-23 21:36:10 -04:00
el-j 71a669635c M2.1: prevent click-through on open path fills and support segment toggle (#3405) 2026-10-24 06:00:23 +07:00
bflatastic c2ad9f8bbb Type: emoji go with type that effects reshape and Expand Appearance
Squashed from #3879 onto main.
2026-10-24 04:49:06 +07:00
Taha-Mahmoodi f365468684 Fix #3233: an opened dimension whose label characters are off the colour mode is redrawn too
Review follow-up (CodeRabbit): a text node's characters carry their own fill
(CharStyle), saved apart from the node's appearance, so conform also checks
the runs of the label. dimension_colours_follow_the_document_colour_model
opens a CMYK document whose label characters alone are RGB; without the
change they stay RGB.
2026-10-23 06:13:00 +07:00
Louise FoxandClaude Opus 5.5 1505384501 Plug-ins: filters keep data with the document (G17)
A filter plug-in had nowhere to keep settings that belong to the whole
document rather than to one object, such as the pens a plotter holds or
a machine profile: it could only ask for them again on every run.

- A filter's output may hold documentData: any JSON of at most 1 MiB
  (objects::MAX_DOCUMENT_DATA) that replaces what the plug-in keeps with
  the document, null removing it, in the same undo step as its objects
  (objects::decode_output). Too much refuses the output and the
  document stays as it was.
- A filter gets it back in _context.documentData (null until it keeps
  something). A filter reads and writes only its own.
- It is the document's foreign data, Document::unknown["pluginData"] by
  plug-in id (vectorcraft_doc::plugin_data), where the perspective grid
  and the brushes keep theirs. So the native file needs no section of
  its own for it, and hosts from before keep it when they save. An
  exporter is given the whole document, so it can read every
  plug-in's. Nothing else lists it, as the reference app's Document
  Info lists no plug-in data.

A live effect isn't given it: its geometry is worked out from the
object alone wherever it is drawn.

Tests: plugin_data.rs (each plug-in its own entry, forgetting what isn't
kept copies nothing, a damaged value holds nothing), objects/tests.rs
(kept, forgotten, refused over the limit), tests_plugins.rs (one undo
step with the objects, read back, not given to another filter, kept by
the native file among its foreign data, refused over the limit with the
document unchanged, null removes it) and tests/plugin_export.rs (an
exporter finds another plug-in's). The size error has its row in each complete
catalog (COMPLETE_MESSAGES).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-23 05:40:00 +07:00
Taha-Mahmoodi 3d24c663d2 Fix #3233: dimensions are redrawn in the colour mode after Document Color Mode and on open
Follow-up to #3702 (CodeRabbit): dimensions are only redrawn when their
recipe changes. Document Color Mode converted their art through the colour
profile, so black became a four-ink C67 M67 Y67 K100 again, and CMYK files
saved before #3702 kept RGB dimension art.

dimension::refresh now also redraws when the document's colour mode
changes. dimension::conform, run when a document is opened or reverted
(Session::add_document, replace_document), redraws a dimension whose art
has colours in the wrong model; one that can't be redrawn keeps its art.

dimension_colours_follow_the_document_colour_model covers RGB to CMYK and
back, and a CMYK document with RGB dimension art being opened. Without the
change it finds C0.67 M0.67 Y0.67 K1 after Document Color Mode.
2026-10-23 04:26:05 +07:00
Taha-Mahmoodi 866bf61116 Fix #3233: in a CMYK document dimension lines and labels are CMYK, black on the K plate only 2026-10-23 03:35:07 +07:00
bflatastic 997dd8d2bd SVG import: images in images share the depth cap, SVGZ images one unpacking cap (#3562, #3563)
Squashed from #3676 onto main.
2026-10-23 01:28:52 +07:00
Taha-Mahmoodi bd6d076484 Effects: Sketch › Conté Crayon draws the dark areas in a black crayon and the light areas in a white one on textured paper 2026-10-22 21:18:34 +07:00
dwetscherandClaude Opus 5.5 2d7004f972 AF.5: cap dash patterns at 64 lengths
A line style's `Patn` was copied twice (filtered, then scaled) whatever its
length, before any budget saw it. Patterns longer than 64 lengths now import
as solid lines, with a warning.

Addresses CodeRabbit's third review on #3548.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:44:39 +07:00
dwetscherandClaude Opus 5.5 b3ccdf01a7 AF.5: nested overlays innermost first; embedded master pages; text outline budget
- Overlays bake child-first: a group's overlay now lies over its children's
  (they used to be applied after it and win). Each one pays out of the
  import's overlay work budget.
- Master page instances inside embedded documents are left out with a warning:
  their indices count the embedded document's masters, which aren't kept, and
  would show the outer document's.
- A text outline's gradient stops and dashes are charged to the text attribute
  budget, like the fill's.

Addresses CodeRabbit's second review on #3548.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:24:57 +07:00
dwetscherandClaude Opus 5.5 f73a3df974 AF.5: bound hostile Affinity files; fuzz the new import paths
- One budget for the whole read (layers, pixels, characters, table
  cells), shared with embedded documents and charged also when their
  reading fails; the first reading is dropped before the second.
- Tables: at most 65 536 cells; lines and cell formats that don't
  match the grid are left out instead of shifted.
- Paragraph formats matched to runs by binary search; font names
  longer than 256 bytes are dropped.
- Overlays: one work budget per import; encoded originals decoded
  within their layer's size; table cell outlines recoloured too.
- Engine: master page copies capped, each page's master found in
  linear time, line gaps looked up once per font, the working CMYK
  table built once, objects past MAX_COORD left out.
- Embedded content stays with its node; pixel masks under artistic
  text are read whole and include stroke reach; gradient overlays on
  hidden layers are kept.
- Render: contexts and raster effect images stay within
  MAX_RASTER_SIDE per side (vello overflowed near u16::MAX; found by
  the new fuzz target).
- Tests: affinity tests/import_limits.rs, feature samples in
  import_fuzz.rs, shared builders in engine/tests/support/.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:24:57 +07:00
dwetscherandClaude Opus 5.5 fd50bd6f84 AF.5: Affinity import — strokes, text decoration, text placement, outline effects, CMYK photos
- Strokes set to None no longer import as black 1px strokes.
- Underline and strikethrough are kept.
- Frame text starts at cap height; lines hanging out of a frame make it taller;
  mixed-alignment artistic text lines up within its longest line; line breaks
  inside a paragraph get no paragraph spacing.
- Table cell text taller than its cell is centred and stays visible.
- Outline effects on stroked shapes are drawn around the stroke.
- CMYK pixel layers go through the document's embedded CMYK profile; without
  one, opaque layers stay CMYK.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:24:57 +07:00
bflatastic 92f295d2a2 Blend: Ease In / Ease Out and separate Color Ease in Blend Options (#1124)
Squashed from #2539 onto main.
2026-10-22 04:40:10 +07:00
bflatastic 10a178f64a Shape Builder: Gap Detection and Tool Options (#2523)
Squashed from #2526 onto main.
2026-10-22 02:53:17 +07:00
bflatastic 755f2b9158 G2: Effect › Artistic › Rough Pastels raster filter
Squashed from #3044 onto main.
2026-10-22 00:23:50 +07:00
bflatastic bbe026607f Variables: Load Variable Library… reads a variable library's text variables and data sets
Squashed from #2853 onto main.
2026-10-22 00:12:39 +07:00
bflatastic 0a3628f62f G2: Effect › Artistic › Dry Brush raster filter
Squashed from #3026 onto main.
2026-10-21 23:38:02 +07:00
Dan Green e8bd0ac410 Distribute: selected ruler guides are spaced with the objects (#1597 in part)
Distribute Objects and Distribute Spacing space the selected ruler
guides across the axis (vertical ones for the horizontal options,
horizontal ones for the vertical options) together with the selected
objects, as items with no size. Guides along the axis stay where they
are. Two or more guides selected without objects are enough, and the
Align panel's Distribute buttons are enabled for them. With Align to
Artboard, the outermost items go to the artboard's edges.

A key guide (select.key {guide}) is one of those items. With a spacing,
it stays put and the other items are spaced from it, as from a key
object with no size; a key guide along the axis leaves everything where
it is. The Align panel sends the Distribute Spacing value with both
rows of Distribute buttons while a guide is the key, and enables the
value for it.
2026-10-21 15:44:27 +07:00