Commit Graph
1766 Commits
Author SHA1 Message Date
dotCooCoo b46cdc4e4d Isolation mode: Lock, Unlock All, Hide and Show All work on the isolated art
Squashed from #4849 onto main.
2026-11-01 01:21:53 +07:00
dotCooCoo ec8c1cb8d1 Intertwine: a right-click where three or more objects overlap arranges the nearest one
Squashed from #4862 onto main.
2026-11-01 00:36:29 +07:00
dotCooCoo d6297dbfb9 .ai import: type comes from the text document alone when every shown story reads
Squashed from #4865 onto main.
2026-11-01 00:35:35 +07:00
dotCooCoo bb6cbfb2b4 Images: SVG and EPS export write colorized grayscale and 1-bit images in their fill color
SVG export ignored an image's colorize paint and wrote the image's own
black, white and gray pixels. EPS export flattened every colorized image
into a process-color raster, so a spot color lost its plate. Both now
write the image in the fill color, with the coverage the canvas and PDF
export use (darkness times alpha), so white and transparent pixels take
no color.

- SVG writes a coverage PNG as a mask over a rectangle in the color,
  shared between repeated images while each keeps its own color. Linked
  mode writes the coverage PNG as a file, and SVG Tiny 1.2 writes a PNG
  in the color. A colorized image whose pixels are missing, or whose
  grayscale pixels can't be decoded, is left out with a warning.
- EPS writes fully covered or uncovered pixels as an imagemask in the
  color's process or Separation ink (Registration on every plate), and a
  grayscale image as ink samples scaled by coverage. Level 3 masks its
  uncovered pixels out and Level 2 writes them white; gray midtones are
  written over white, with the existing warnings.
- EPS flattening no longer counts a solid colorize paint on a grayscale
  or 1-bit image as transparency by itself. Opacity, blending modes,
  opacity masks, effects and overlapping transparent art still flatten
  it, and so does a colorize paint the writer can't draw.

The svg crate's tests turn on resvg's raster-images feature to render
the embedded PNGs.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-01 00:35:27 +07:00
dotCooCoo 93e27cafd3 PDF/EPS export: live art expanded for pattern tile images counts against the object limit
Blends, envelopes, repeats and Intertwine groups that hold a pattern fill
with a transparent tile image were each expanded under a limit of their
own, so many of them together had no limit. Their art now counts toward
the same limit as the pattern tiles, and an object whose art would pass
it stays live.
2026-11-01 00:35:23 +07:00
dotCooCoo a32a7cbb0d PDF/EPS export: EPS export and PostScript printing expand pattern tile images once
EPS export expanded the pattern fills whose tile images need flattening and
then flattened the result with flatten_document, which expanded them again
with a new object limit. PostScript printing did the same, on the layers
shown rather than the layers that print. EPS export now flattens the
expanded document without expanding it again (flatten_expanded), and
PostScript printing leaves the expansion to the flattener, which runs on
the layers that print, and only checks for such pattern fills to warn.
2026-11-01 00:35:23 +07:00
dotCooCoo e2fddc6b12 PDF/EPS export: pattern fills whose tiles pass the object limit stay pattern fills
The expansion counted the objects a pattern fill made only after making
them, so one shape could clone up to 250,000 tile positions times the
tile art's objects past the limit. Each fill now counts its tiles before
they are made, and after outlining strokes and effects for turned or
sheared tiles, and keeps room for the copy and group of the fills after
it. A fill that does not fit stays a pattern fill.
2026-11-01 00:35:23 +07:00
dotCooCoo c09ea59731 PDF/EPS export: transparent images in patterns nested in tile art flatten
A pattern fill in a pattern's tile art that paints a see-through or
colorized image was not counted, so PDF 1.3 export kept it as a
pattern and failed on its transparency. Detection now follows pattern
fills in tile art down to the three levels the canvas draws, skipping
hidden tile art, and the tiles the expansion makes are expanded again
for them. Expansion stops once the expanded art holds MAX_GENERATED
objects.
2026-11-01 00:35:23 +07:00
dotCooCoo 6dfda4db76 PDF/EPS export: pattern tile images in live art and hidden shapes flatten as drawn
Blends, envelopes, repeats and Intertwine groups that hold a see-through
or colorized pattern tile image are expanded into the art the writers
draw for them before the tiles are expanded, so those tiles are
flattened too. Random Transform effects in that art are baked first.

Hidden shapes, shapes in hidden groups or layers and template layers are
skipped: expanding a hidden shape made its tiles show in the file, and
art that is not written no longer makes a flattened copy of the document.
2026-11-01 00:35:23 +07:00
dotCooCoo 7b4d6282f1 Flattening: an option leaves colorized images to writers that draw them
Flattening counted every colorized image as transparency, in the art and
in pattern tiles. A new flattener option, keeps_colorize, stops counting
an image colorized with a solid paint whose pixels are grayscale or 1-bit,
for a format whose writer draws such images in their ink. Opacity,
blending modes, opacity masks and effects still flatten them, and pattern
tiles are still expanded for those. The option is off, so exports flatten
as before; the EPS writer that draws colorized images turns it on.

The EPS pattern tile tests now check the exported colors, and a tile
group with opacity for the warning.
2026-11-01 00:35:23 +07:00
dotCooCoo 33ab5adbc2 PDF/EPS export: flatten see-through and colorized pattern tile images
PDF 1.3, PDF/X-1a, PDF/X-3, EPS and PostScript printing flatten
transparency with `flatten_document`, which only looked at art in the
layers. An image with see-through pixels, a colorize paint, opacity or
a blending mode drawn only through a pattern fill stayed in the pattern
definition, so the flattener returned early and the writer drew the tile
opaque and uncolorized (PDF 1.3 export failed on the transparency left).

`flatten_document` now first expands pattern fills whose tile image
needs flattening into their tiles, as legacy .ai save does. See-through
tile pixels count only where the caller counts see-through images (PDF
1.3); EPS level 3 already masks them out. The EPS and PostScript print
transparency warnings now see those tile images too.
2026-11-01 00:35:23 +07:00
Wadah Badawi c35b51eb0f Intertwine: object.intertwine.arrange takes an id without a selection 2026-10-31 23:48:56 +07:00
Wadah Badawi 6eea3a5178 Pathfinder: Crop works on a selected group of shapes 2026-10-31 23:48:55 +07:00
dotCooCoo e8b1f1187d Tests: a pixel layer nested in Affinity text stays an image inside the clip
objects_nested_in_text_are_clipped_by_the_type now also nests a 4 x 4 Rstr pixel layer under the TxtA and checks that it is read as an image in the clipping group, next to the shape.
2026-10-31 23:29:39 +07:00
dotCooCoo a48ab7f8c6 Fix #3325: art nested in Affinity text is clipped to the characters
An Affinity text object whose children are nested in it (an image dragged
into artistic text, for example) opened as a plain group, so the image
was drawn over the text. The text now becomes a clip group whose
clipping path is the type, as a shape with children already does: the
nested art shows only inside the characters, with the characters' fill
behind it and their stroke over it.
2026-10-31 23:29:39 +07:00
dotCooCoo 77bdf478e4 Perspective: a homography product that overflows is refused instead of stored or drawn
Homography::then_after returned products whose entries could overflow to
infinity or become NaN. A move, transform or grid reproject of type or a
symbol instance whose stored projection has large finite entries stored such
a product, and the type was then drawn flat, since a projection that is not
finite reads as none.

- Homography::checked_then_after returns None when an entry of the product
  is not finite.
- project_node composes each type and symbol instance projection with it and
  returns false, leaving the object as it is, when one would not be finite;
  moves and transforms in perspective and Define Perspective Grid with
  reproject then return the horizon error and leave the document unchanged.
- transform_map, attach_homography and size_homography return None for such
  a product.
- Edit Text returns the horizon error, and does not enter isolation mode,
  when the type cannot be shown flat where it is drawn.
2026-10-31 23:29:37 +07:00
dotCooCoo f9e78eb144 Appearance: effect.apply reports the active row's object, and an empty aligned target list is an error
effect.apply now reports the item of the first selected object, whose row
the active item is. With nothing selected, an explicit aligned item returns
the command's error instead of an internal error.
2026-10-31 23:29:35 +07:00
dotCooCoo a32d6364ec Appearance: paint, stroke and effect edits change the active row of the first selected object
The Appearance panel's active row is a row of the first selected object's
stack, but fill, stroke, gradient and effect edits matched it against the
object that paints first. When the selection was made top object first and
the objects' stacks differ, an edit changed another row: with the red fill
of [red, white, Stroke] active above a [white, Stroke] object, Fill set the
white row instead.

These edits now match the active row against the first selected object (or
the compound path holding it), as Add New Fill and Reduce to Basic
Appearance already do. Gradient edits also read whether the active row is a
fill or a stroke from that object.
2026-10-31 23:29:35 +07:00
bflatastic f0da383749 Smooth: the slider smooths bumps and jags into curves, and smooths only a direct-selected section 2026-10-31 23:10:56 +07:00
bflatastic cd2b5f77a4 Fix #4882: Rasterize to CMYK makes a CMYK image 2026-10-31 22:22:27 +07:00
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 b8e5f7088f Export for Screens: Export PDFs As Single file writes the artboards as one PDF's pages 2026-10-31 21:43:13 +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 78a68bd8ec Fix #4835: Artwork Cleanup's Duplicate Objects finds type set twice in the same place 2026-10-31 19:43:17 +07:00
NetterSilverandClaude Haiku 5.5 7ae9f2c4e5 Keep an empty text value in dataset CSV export and import
Found while testing crates/engine/src/cmd/variablecmds/csv.rs:146 (csv_escape) and :158-163 (cell_to_value)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 19:43:17 +07:00
marshiyar 4861fb9d85 Engine guard: a recovered panic restores every document's undo history and the active document (#3599)
run_guarded put back only the active document's art, selection and interaction.
An undo step pushed before the panic stayed (Undo then stepped to a state equal
to the current one) and Redo was lost; other documents a command touched kept
their half-applied edits; the active document was restored by index.

It now snapshots every open document with its history and restores them, and
the active document by uid.

Fixes #3599
2026-10-31 19:43:17 +07:00
Taha-Mahmoodi 811ba8727b Fix #3604: automation file access refuses an NTFS alternate data stream name on Windows
With write root ...\out, a save to ...\out\x.svg:evil was accepted: it stays inside the root, but writes a hidden stream no folder listing shows. A ':' in any name after the drive is now refused with the device names. The check is a plain name test, so it is covered on every platform; the check-then-open race (item 1 of the report) is a documented limitation and is not touched.
2026-10-31 19:43:17 +07:00
marshiyar feb12cf312 Swatch libraries: a device name with an extension gets the - before its first dot (#3602)
CON.txt became CON.txt-.json, whose first part is still CON. It is now CON-.txt.json.
2026-10-31 19:43:17 +07:00
marshiyar 64ffab00a9 Swatch libraries: a library titled with a Windows device name is saved under another name (#3602)
library_file_name kept CON, NUL, COM1 and the like, which Windows treats as
devices, not files. It now adds a - after such a name (CON-.json).

Part of #3602
2026-10-31 19:43:16 +07:00
Don Omondi 9259434a89 SVG export: a gradient mesh patch is cut no finer than its colour changes
Each patch of a gradient mesh was cut into 8 x 8 flat quads for SVG export,
Object > Expand and the other outputs that cannot shade a mesh, whatever its
colours, so a patch of one colour became 64 identical pieces. Neighbouring
quads are now joined into square blocks wherever the colour changes by no
more than about one 8-bit step across the block; the block's outline runs
through its quads' corners, and a patch that folds over itself is not joined.
2026-10-31 19:43:16 +07:00
dotCooCoo ae76455723 Perspective Selection: a move of loose art keeps its plane when 4 clears the active plane
The move drag remembers the plane it places loose art on. Pressing 4
during the drag, or dragging on after it, previews and commits the move
on that plane instead of dropping it. A 1-3 key pressed before the drag
threshold also sets the plane the move uses.
2026-10-31 19:43:16 +07:00
dotCooCoo c15874ba6e Perspective Selection: Shift constrains each object in its own plane
perspective.move takes constrain: true instead of a plane-space
direction. For each object, the command snaps the move to the nearest
axis or 45-degree diagonal of that object's own plane, so a selection on
several planes keeps every object on a line of its plane. The tool sends
the raw pointer with the flag.

On a diagonal, Snap to Grid now moves along the diagonal by the nearer
per-axis correction, so that edge lands on its gridline.
2026-10-31 19:43:16 +07:00
dotCooCoo 9f64452303 Perspective Selection: Snap to Grid keeps a Shift-constrained move on its axis or diagonal
perspective.move takes an optional constrain: [dx, dy], a plane-space
direction. For an in-plane move, Snap to Grid's correction is projected
onto that direction, so a move the Shift key constrains to a plane axis
or a 45-degree diagonal stays on that line when an object edge lies near
a gridline. The Perspective Selection tool sends the direction for
Shift moves and copies. Moves without the param behave as before.
2026-10-31 19:43:16 +07:00
dotCooCoo f677c60aba Perspective Selection: Shift constrains a move to the plane's axes and diagonals
Holding Shift while dragging objects with the Perspective Selection tool
snaps the move, in the plane's own coordinates, to the nearest plane axis
or 45 degree diagonal. This applies to plain moves and to Alt and
Ctrl+Alt copies. The preview, the final move and the measurement label
use the snapped point. Pressing or releasing Shift, pressing 5 and
pressing 1 to 4 during the drag recompute the preview from the pointer;
a move perpendicular to the plane is not constrained.
2026-10-31 19:43:15 +07:00
dotCooCoo 45b266c805 Appearance: Reduce to Basic Appearance finds the active row in the first selected object
The targets are in paint order, so the first selected object is not always the first target. The active row is now matched against the first selected object wherever it is among them.
2026-10-31 19:43:15 +07:00
dotCooCoo d9fefde061 Appearance: Reduce to Basic Appearance keeps the selected fill or stroke row
Reduce to Basic Appearance kept the topmost visible fill and stroke even
when another fill or stroke row was selected in the Appearance panel.
It now keeps the selected row in place of the topmost one of its kind,
and in each other selected object the row equal to it. With ids, or
with no selected row, it keeps the topmost ones as before.
2026-10-31 19:43:15 +07:00
dotCooCoo 3a7e81e6c3 Appearance: Add New Fill copies the selected fill row right above it
With a fill row selected in the Appearance panel, Add New Fill now
inserts a copy of that row directly above it and selects the new fill.
Rows already above the selected one stay where they are. Before, the
new fill was always a copy of the topmost fill, added at the top.

The selected row belongs to the first selected object. In a mixed
selection, an object that shares that row gets the copy above its own
equal row; an object that does not share it gets a copy of its top
fill on top, as before. With a stroke row selected, or with `ids` or
`target: "contents"`, Add New Fill works as before.

The panel drops rows that were Cmd- or Shift-clicked together when the
active row changes outside the panel, so after Add New Fill (button,
menu or shortcut) only the new fill is selected.
2026-10-31 19:43:15 +07:00
dotCooCoo 65b6606d90 Part of #3229: setting type in a font or style that isn't available warns
Squashed from #4866 onto main.
2026-10-31 19:43:15 +07:00
dotCooCoo 1f31b6ed4a Find Font: Change and Change All leave hidden and locked text alone
Squashed from #4852 onto main.
2026-10-31 19:43:15 +07:00
dotCooCoo 875d3301f5 Transparency: Make Opacity Mask over several layers masks their art
transparency.makeOpacityMask with two or more layers below the mask
object (or a layer and objects of other layers) wrapped the layers
themselves in a new top-level group, so a layer ended up inside a
group. A layer among several masked items now stands for its art: the
masked group takes the layer's visible, unlocked objects, a hidden,
locked or template layer gives none, and the layers stay where they
are. One layer under a mask object, or one layer alone, is masked as
before.
2026-10-31 19:43:15 +07:00
dotCooCoo baaffd63e7 Blob Brush: Tool Options set the brush's Angle and Roundness
Blob Brush Tool Options gain Angle (-180 to 180 degrees, 0 by default)
and Roundness (0 to 100 percent, 100 by default), with a preview of the
tip and three dabs; a drag in the preview square sets the Angle. The
elliptical tip keeps its angle on the page along a stroke, a 0 percent
tip is drawn 0.1 percent as wide as it is long, and Size set to
Pressure still varies its size. path.blob takes angle and roundness,
and both values are kept with the tool's options.

While a non-round tip drags, the Blob Brush shows the outline of the
area it covers with its length label, and an Alt drag that smooths
shows the Smooth tool's thin line.
2026-10-31 19:43:15 +07:00
dotCooCoo d318a77560 DXF export: Alter Paths for Appearance outlines only strokes a CAD line can't draw
Alter Paths for Appearance wrote every stroke as its filled outline, so a
1 pt circle became two circles and a hatch, and an open arc became a
closed outline and a hatch: two cut lines where a CAM program expects
one. The option now outlines only the strokes a line can't draw: the ones
Preserve Appearance outlines, strokes wider than the widest lineweight
(2.11 mm, to the hundredth), and every stroke in R13 and R14, which have
no lineweights. Other strokes stay CIRCLE, ARC, SPLINE and LWPOLYLINE
entities with their lineweights; R12 keeps them as POLYLINEs with their
width.

A dashed stroke whose offset starts part way into its pattern is now
outlined with Preserve Appearance and with Alter Paths: a linetype always
starts at the start of its pattern, so the dashes were written in the
wrong place.
2026-10-31 19:43:15 +07:00
dotCooCoo 06e8d711ae DXF export: an open path of one straight segment is written as a LINE
An open path with a single straight segment was written as a two-vertex
LWPOLYLINE (POLYLINE before R14). It is now a LINE entity from its start
to its end point. A stroke with a weight in R12, R13 and R14 keeps its
polyline, since a LINE has no width. Closed paths and paths with more
than one segment are unchanged.

Part of #1299.
2026-10-31 19:43:15 +07:00
dotCooCoo 9571ae0a46 Outline Stroke, Expand: hidden and locked objects stay hidden and locked
The art that replaces an outlined or expanded path did not take the
path's hidden, locked or Knockout Shape state. Outline Stroke on a
hidden path, or Expand on a group with a hidden member, made the new art
visible (a filled and stroked path became half visible: the outline
shown, the fill copy hidden), and a locked member's art was unlocked.

The replacement now takes the path's visibility, lock and Knockout Shape
state, and the fill copies and clipping paths inside it are shown and
unlocked, so showing or unlocking the new art shows or unlocks all of it.
This covers Outline Stroke, and Expand's strokes, gradient fills and
pattern fills.
2026-10-31 19:43:15 +07:00
dotCooCoo 9e8e091c86 Find Font: Find leaves out hidden and locked text
select.font (the Find Font dialog's Find button) selected every text
object using the font, including hidden or locked text and text in a
hidden or locked group or layer. It now selects only text that is
visible and unlocked through every group and layer, and not on a locked
artboard. Text on a visible, unlocked template layer is still found. A
font used only by hidden or locked text selects nothing.
2026-10-31 19:43:15 +07:00
dotCooCoo 7860931a3b Select Same: Fill Color from a Live Paint face stays in its Live Paint group
Select > Same > Fill Color and Fill & Stroke from a face of a Live Paint
group now select the matching faces of that group only, and from any
other object they select no Live Paint face. Before, they matched faces
of every Live Paint group and paths outside them alike.
2026-10-31 19:43:14 +07:00
dotCooCoo 9163c22396 Fix #4819: Convert to Smooth and Corner convert a selected blend's spine
With a blend selected, the Contextual Task Bar's Corner and Smooth
buttons failed with "select paths or anchor points", since
`path.convertAnchors` only read paths. They now convert every point of
the blend's spine, as they convert every anchor of a path selected
whole, in one undo step. The key objects stay where they are.

With Direct Selection, the Control bar and the Properties panel now
show their Convert buttons for a selected blend too.
2026-10-31 19:43:14 +07:00
dotCooCoo 462b7cca15 Image Trace: Document Library fills are not linked to swatches
With Document Swatches as the library, traced fills and strokes take the
library color without a swatch link. Global, spot and tint swatches no
longer link the trace, so editing a swatch later leaves the trace
unchanged, and swatch order no longer decides whether a fill is linked.
Each region still takes the nearest library color, and a tint traces at
the color the tint swatch shows.
2026-10-31 07:17:08 +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
dotCooCoo 82025e57b5 Live Paint: Alt-click and Alt+Shift-click with the Bucket sample with the Eyedropper
With the Live Paint Bucket, Alt-click on vector art samples its fill,
stroke and stroke weight into the paint defaults, and on a placed image
it samples the pixel as a solid fill with no stroke. Alt+Shift-click
samples only the color under the pointer, including an intermediate
gradient color or image pixels, into the active fill or stroke. Neither
click paints or changes the selection or selected art, and the next
Bucket click paints with the sampled paint. While Alt is held, the
Bucket shows the Eyedropper cursor and no face or edge highlight.

appearance.copyFrom takes `defaultsOnly: true` to update only the paint
defaults, and paint.sampleColor takes `clearStroke: true` to clear the
stroke default after an image sample into the fill.
2026-10-31 06:28:46 +07:00