Commit Graph
989 Commits
Author SHA1 Message Date
dotCooCoo bc8e42d8c7 Export: the art's bounds take in the effects of groups, type and images
The bounds of the visible art (`vectorcraft_render::encode::art_bounds`)
are the crop of Export with Use Artboards off, EPS without artboards,
Export for Screens' Full Document and Save for Web (Legacy) with Clip to
Artboard off, and the size Fit to Artwork Bounds gives an artboard. They
measured a group or layer by its members and left out its own effects,
and measured type and images without their effects. A group's drop
shadow, or the shadow of a line of type, was cut off at the image's edge
and left outside the fitted artboard. They also counted a Shaper group's
transparent source art.

Each visible object is now measured by `vectorcraft_render::effect_bounds`,
the measure of Export Selection's crop. A group or layer without effects
of its own is still measured by its visible members, skipping a Shaper
group's source art. A group or layer with a drop shadow, a glow or another
raster effect is measured without its hidden members, guides and template
layers, unless it is a Shaper group or also has a geometry, Pathfinder,
Crop Marks or 3D effect or a visible effect on its own fill or stroke.
The SVG of a chosen artboard also keeps an object whose effects alone
reach the artboard.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 15:27:54 +07:00
dotCooCoo d973805269 Type: geometry effects on a type object's own fills and strokes are drawn and exported
A geometry effect applied to one of a type object's own fills or
strokes (effect.apply with item) was kept but not drawn or exported
unless the type also had a geometry effect or perspective of its own.
With a geometry effect, the renderer measured the type for culling
without the fill's or stroke's effects, so an outline that reached past
the glyphs was not drawn once the glyphs left the view.

Type whose own fills or strokes have a visible geometry effect is now
drawn, culled and exported through its outlines, the same way as type
with an object-level geometry effect. Each effect reshapes the whole
outline that its fill or stroke paints. SVG, PDF, EPS and the other
vector exports write such type as paths. Type that holds inline
graphics stays type, as before.
2026-10-16 15:27:54 +07:00
dotCooCoo 56acb8d3b6 Select: Update Selection replaces the recalled saved selection with the current selection
The Select menu had no Update Selection: to add objects to a saved
selection or take objects out of it, the selection had to be saved
again under the same name. Select › Update Selection (`select.update`)
now gives the saved selection last recalled the objects that are
selected, in one undo step. It is enabled while objects are selected
and the document still holds that saved selection.

Each open document keeps session ids for its saved selections, so the
recalled one is found under a new name after Edit Selection…, and after
Undo and Redo. Deleting it disables Update Selection until another
saved selection is recalled or the delete is undone.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 15:27:54 +07:00
Marco Arnulfo 403b335039 Effects: Effect › Sketch › Halftone Pattern redraws an object through a halftone screen of dots, rings or lines 2026-10-16 15:27:53 +07:00
dotCooCoo ddfd08f321 Blob Brush: Keep selected and Merge only with selection options
A Blob Brush stroke merged with every touching closed path of its
color that had no stroke, and the blob it drew or merged into was
always left selected. The Blob Brush Tool Options had Size only.

The Blob Brush Tool Options now have Keep selected and Merge only
with selection, both off by default. A blob that a stroke draws or
merges into is no longer left selected unless Keep selected is on,
and the rest of the selection stays as it was. While Merge only with
selection is on, a stroke merges only with selected blobs, and with
nothing selected it draws a new blob. The options persist like the
tool's Size, and path.blob takes keepSelected and mergeOnlySelection,
both false by default. Keep selected stays on by default for the
Pencil and Paintbrush.

Docs: docs/target-app-parity.md, docs/control-protocol.md.
2026-10-16 15:27:53 +07:00
dotCooCoo 3b683bbeec Ellipse: dragging a pie widget on the canvas sets its pie angle
The Selection tool shows two pie widgets just outside a selected live
ellipse while the bounding box shows: one at the Pie Start Angle and
one at the Pie End Angle. Dragging one sets its angle to the angle of
the outline's point that lies in the direction of the pointer from the
ellipse's center, in one undo step, through `object.setLiveShape`'s
`pieStart` or `pieEnd`. Double-clicking one makes the ellipse whole
again (0° to 360°). Where the two widgets coincide, as on a whole
ellipse, a press takes the End widget.

Docs: docs/target-app-parity.md, docs/ui-parity.md.
2026-10-16 15:27:53 +07:00
Fabio 38dd835af6 G5: Symbol Options Export Type (Graphic / Movie Clip)
Store Export Type on symbols for Animate handoff; New Symbol and Symbol Options set it, and symbol.list reports it.
2026-10-16 15:27:52 +07:00
dotCooCoo 21b4e70150 Shaper: an arrow widget switches a Shaper Group between Face Selection and Construction mode
A Shaper Group had no arrow widget for switching between Face Selection
and Construction mode.

With the Shaper or Selection tool active, a selected Shaper Group, or a
group with one of its faces selected, now shows an arrow beside the
middle of the right side of its source shapes' bounds. A click on it
isolates the source shapes (object.isolate). A second click leaves
isolation with the group selected (object.exitIsolation). The arrow
points right in Face Selection mode and left in Construction mode.

In Construction mode a Shaper click selects an original shape of the
isolated group. Before, it selected the whole group outside the
isolation, and shaper.select with source could pick an original of
another Shaper Group lying above it.

Docs: docs/shaper.md, docs/target-app-parity.md.
2026-10-16 15:00:07 +07:00
dotCooCoo 6dfdf643d9 Type: Cmd/Ctrl+Shift+X resets horizontal scale to 100%
The reference app resets the horizontal scale of type to 100% with
Cmd/Ctrl+Shift+X. VectorCraft had no command for it, and the chord
cut the selection: egui reports it as a Cut with Shift held.

type.resetHorizontalScale (Reset Horizontal Scale, default
Cmd+Shift+X) sets the horizontal scale to 100% in the text the Type
tool has selected, or else in all the text of every selected type
object, in one undo step. The value is the one the Character panel
shows, so a type object stretched to twice its width keeps 50% stored.
The vertical scale does not change. At a caret the command leaves the
text as it is. Type objects given in ids are reset in full, as
type.resetTracking does.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 14:58:09 +07:00
dotCooCoo ad1e8d1112 Type: Cmd/Ctrl+Alt+Shift+. and , step the font size by five increments
No command held Cmd/Ctrl+Alt+Shift+. and Cmd/Ctrl+Alt+Shift+,, so the
chords reached Increase and Decrease Font Size (Cmd/Ctrl+Shift+. and ,)
and changed the size by one Size/Leading increment.

Two commands, type.size.increaseByFive and type.size.decreaseByFive,
now hold the chords. They step the font size of the selected type, or
of the text the Type tool has selected, by five times the Size/Leading
increment (Preferences › Type). Agents reach them with press_key too.
Their labels have rows in every complete catalog.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 14:58:09 +07:00
dotCooCoo f46b0f7dc4 PDF export: a page larger than 14,400 pt is written with a UserUnit
PDF export and .ai saves wrote every page in points. ISO 32000-1
(Annex C.2) gives 14,400 units as the longest page side, and a page
with a longer side got a media box past that limit. A PDF/A-2b file
with such a page failed veraPDF's page size rule (6.1.13).

Such a page now gets a UserUnit (PDF 1.6): its longer side divided by
14,400 and rounded up to the next thousandth, with the page's sizes
and coordinates in those units. A 20,000 × 10,000 pt artboard is
written as a 14,398.85 × 7,199.42 page with /UserUnit 1.389, and
VectorCraft's PDF import reads it back at 20,000 × 10,000 pt. Pages
up to 14,400 pt, PDF 1.3 to 1.5 files, PDF/X files and printing are
written as before.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 14:58:09 +07:00
dotCooCoo cc3299dc0f Artboards: Fit to Artwork Bounds fits each selected artboard to the art on it
Fit to Artwork Bounds resized the active artboard to the bounds of all
the art in the document, wherever that art was. With art on two
artboards, fitting one of them stretched it over both.

artboard.fitToArt now takes `indices` and fits each artboard to the
art on it, in one undo step. An object counts for every artboard its
drawn bounds overlap, and the artboard is fitted to all of its bounds,
also where it reaches past the artboard's edge. Art flush with an edge
from outside does not count. An artboard with no art on it keeps its
size and place, and the command fails with "no artwork" when none of
its artboards has art. Object › Artboards and the Artboards panel menu
pass the selected artboards (the Artboard tool's, else the active one),
and a recorded action leaves them out. Fit to Selected Art still
resizes the active artboard.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 14:58:09 +07:00
dotCooCoo ebc2857549 Align: Distribute Objects uses the spacing value with a key object
With a key object, the Align panel's six Distribute Objects buttons
ignored the Distribute Spacing value. They kept the outermost objects
in place, spaced the others evenly between them and could move the
key object.

object.distribute now takes spacing. While the selection has a key
object, the key object stays in place, with the compound path or
selected group that contains it, and the objects' left edges, centers
or right edges (top edges, centers or bottom edges) end up spacing
points apart, outward from the key object's, in their order on that
line. With 0, the field's default, every object ends on the key
object's line. Two objects are enough, and to and artboard are not
used. The panel sends the value with the Distribute Objects buttons
while there is a key object, as it does with the Distribute Spacing
buttons. Without a key object, or with anchor points selected,
object.distribute does not use spacing and works as before. A spacing
that is not a number is an error.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 14:58:08 +07:00
VictorandClaude Opus 5.5 d049e69b95 G1: Remove Anchor Points selects the next anchor (#2241)
After Object › Path › Remove Anchor Points the anchor that follows each
removed one is selected: round the end of a closed subpath, or the one
before it at an open subpath's end. Running the command again goes on
along the path, without picking each anchor first. Several anchors,
subpaths and paths each get their own next anchor; a path that goes
leaves nothing selected; one undo step restores the anchors and the
selection.

The Delete key still selects the remainder whole so a second Delete
removes it, and the Delete Anchor Point tool still leaves the path
selected whole.

Fixes #2241

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 14:58:08 +07:00
Marco Arnulfo bb66070841 Anchor Point tool: a click on a handle end retracts that handle only (fixes #2267) 2026-10-16 14:58:08 +07:00
dotCooCoo 88088e99bb Variables: selecting a data set reports the area type it overflows
dataset.select, dataset.next and dataset.prev rewrite bound type with
the data set's text but replied only {name, applied, skipped}. A long
value could push lines out of a fixed frame and the reply had no
overflow key, while text.setText answers overflow: [id] for the same
text.

The three commands now add overflow: [id...] with the area type and
type on a path whose text they rewrote and that doesn't fit, through
typecmd::with_overflow, as text.setText does. An object that two text
variables rewrite is named once. Type that the data set left alone is
not named, and the key is left out when all of the rewritten text
fits.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 14:58:08 +07:00
dotCooCoo 837d2b5667 Variables: a quoted text cell in a data set CSV keeps its leading and trailing spaces
dataset.import trimmed every cell, quoted or not. dataset.export quotes
a text value with leading or trailing spaces, so a value such as
"  padded " came back from its own export as "padded".

The CSV reader now trims an unquoted cell as before and keeps a quoted
cell as written between its quotes, spaces included. White space
between the closing quote and the next comma or line end is still
dropped. Image, graph and visibility cells, data set names and header
cells are trimmed as before.
2026-10-16 14:57:25 +07:00
Marco Arnulfo 80b7aee756 Isolation mode: a path of the isolated compound path moves on its own (fixes #2354) 2026-10-16 14:57:22 +07:00
NetterSilverandClaude Haiku 5.5 e510190867 Keep Layers panel selection inside the isolated group
Fixes #2286

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 14:57:21 +07:00
NetterSilverandClaude Haiku 5.5 ee6cd6de55 Keep paragraph styles aligned in Find and Replace with line breaks
Found while testing crates/engine/src/cmd/typemenu.rs:668 (find_replace / edit_runs)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 14:57:20 +07:00
bflatastic a64a5a9911 UI: Classic colour scheme (User Interface › Color Scheme)
A second colour scheme next to Modern: the neutral greys and blue accent of
earlier versions, on the same cards, corners and layout. Picked in
Preferences › User Interface › Color Scheme (pref uiColorScheme:
modern|classic); Accent Color is dimmed while Classic is chosen, which has
its own blue.
2026-10-16 14:29:33 +07:00
bflatastic 257217a2e3 UI: restyled interface with a choice of accent colours 2026-10-16 09:00:14 +07:00
Fabio William Conceição b04e3ac366 G12: Collect for Export from Layers panel and layer ids
Layers ≡ and row menus collect art via assets.add; a layer id expands to the art under it (including sublayers) so MCP and the panel match Object › Collect for Export.
2026-10-16 06:16:36 +07:00
sanyamsingh001andCodex 60c8b8f4e2 G1: align guide snapping and readouts with ruler origin
Co-authored-by: Codex <noreply@openai.com>
2026-10-16 06:16:36 +07:00
dotCooCoo 67a278b053 Commands: ids leave out an object listed with a group that holds it, as the selection does
Commands that read the selection through edit::selected_roots leave
out an object whose group is also selected (edit::roots_of). Fourteen
places that read `ids` kept an object listed together with its group.
Given ids that list an object and a group that holds it, Make Repeat
failed with "no such object", New Symbol failed or stored the object
twice in the symbol, and object.transform moved it twice (20 pt for a
10 pt move).

These places now pass explicit ids through edit::outermost, which
drops an id whose ancestor is also listed, with the filter that
roots_of runs on the selection (now edit::without_inner). Nothing else
is dropped or replaced: unlike roots_of, it keeps layers and
compound-path members. A list without nesting gives the same result as
before. Commands that take both ids and the selection as listed, such
as paint.setFill without item, are unchanged.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 06:16:35 +07:00
dotCooCoo c586cecf75 Effects: Glass loads its texture from an image file
Effect › Distort › Glass… offered only its four built-in textures. The
reference app's Texture menu also has Load Texture, which takes an
image file as the glass surface.

The Glass dialog now has a Load Texture… button. It reads a PNG, JPEG,
GIF, WebP, TIFF, BMP, PSD or PSB file within the pixel allowance the
renderer draws images in, and the Texture menu gains Loaded Texture.
The effect stores the image's gray levels in `textureImage` as a
grayscale PNG in base64, so the document saves the texture with the
effect, and copies of the effect carry it. effect.apply and
effect.setParams take `textureImage` as {path}, {name, dataBase64} or
the stored string; any other value is an error.

The texture lies one image pixel per point at Scaling 100%, centered on
the object and repeated. Each pixel's luma is the surface's height,
from 0 for black to 1 pt for white, and a transparent pixel counts as
middle gray. A Glass without `textureImage` draws and saves as before.

Docs: docs/mcp.md (Glass textures), docs/effects-parity.md,
docs/gaps.md, docs/target-app-parity.md.
2026-10-16 06:16:35 +07:00
dotCooCoo 7e9d0be723 Fields: unit fields take % in arithmetic, as in 3cm*50% and 50pt+25%
Unit fields and length strings read a percentage operand as a share of
a value. After * or /, the percentage scales the term before it:
3cm*50% is 1.5 cm and 3cm/50% is 6 cm. After + or -, it adds or takes
away that share of everything before it: 50pt+25% is 62.5 pt and
100pt+20pt-50% is 60 pt. Before this change these expressions read as
nothing, and the field kept its old value.

The parser now splits a sum at its last + or -, so a percentage applies
to the whole sum before it. Text without % gives the same values as
before, apart from float rounding. A percentage on its own (50%) still
reads as nothing, and percent and degree fields still read % as their
suffix.

Command length strings use the same parser: file.new's width and
height, the distance params and the length preferences.

Docs: docs/mcp.md, docs/ui-parity.md, docs/target-app-parity.md.
2026-10-16 06:16:35 +07:00
dotCooCoo 146c43267a Tests: the PDF import fuzz covers values the reader can't parse and functions inside other objects
A new import fuzz test, pdf_resources_the_reader_cannot_parse_never_panic,
imports a page whose resources hold:

- a value the reader can't parse (/#zz, 12-.5) or one it reads (/Fine)
  in the color space dictionary, before a Separation space the page
  paints in, and in the font dictionary, before the font of its text;
- a calculator function written as the shading's function, or inside an
  array or a dictionary in the page's /Properties: a plain one, one with
  Columns 0, one with a huge Columns, or one of 257 MiB of deflated
  spaces.

The import must not panic. The file opens with its art, or it is refused
with "would decode to more than 256 MB" when the function is not plain.

With the nesting check, the font scan or the ink scan reading
Dict::entries again, the test fails on a value the reader can't parse.
With the stream check made on the file's objects alone, the test fails
on every function that is not plain: the file opens instead of being
refused.

The calculator helper of pdf_streams_the_reader_would_fail_on_are_refused
is now a function of the file, and the function of spaces is made once.
2026-10-16 06:16:35 +07:00
dotCooCoo 3b327d9512 EPS import: CMYK images keep their ink amounts
The PostScript interpreter that reads EPS and PostScript files turned
the samples of a CMYK image into display RGB and stored an RGBA PNG,
so the image lost its ink amounts. The DCTDecode filter decoded a CMYK
JPEG to RGB, so it lost its ink amounts too. The PDF importer keeps
the ink amounts of 8- and 16-bit CMYK images (in a CMYK TIFF, or the
CMYK JPEG itself).

A CMYK image (image or colorimage in DeviceCMYK, CIEBasedDEFG or a
four-component ICCBased space) now keeps its samples in a CMYK TIFF
(ImageBlob::cmyk_tiff), read through its Decode array, at every bit
depth the interpreter reads. A CMYK JPEG gives its samples as stored,
and the image's Decode array reads them: [1 0 1 0 1 0 1 0] undoes the
inverted samples of Adobe's CMYK JPEGs. The pixels that a MaskColor
or an ImageType 3 mask hides become a grayscale opacity mask over the
image, as in PDF import. A mask that hides nothing adds no mask. Gray,
RGB, indexed and spot color images and image masks stay RGBA PNGs.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-10-16 06:16:35 +07:00
dotCooCoo 28cb75cc59 .ai import: 1-bit RGB and CMYK images in the editing data open in their colors
The editing data reader sized each row of a 1-bit image as width ×
channels bits, padded to a whole byte, but read one bit per pixel and
made the pixel gray from it. A 1-bit RGB or CMYK image came out as a
black and white image of the wrong bits. When that made the layers
differ from the page by more than 5%, the file opened from its PDF
part instead, with a warning.

The reader now takes channel c of pixel x from bit x × channels + c of
the row, a set bit being the channel at full strength, and converts
the samples as it converts 8-bit ones. 1-bit gray images read as
before.
2026-10-16 06:16:35 +07:00
dotCooCoo 261b64251d PDF/.ai import: PDF and .ai files open with the File Info they carry
A PDF, PDF-compatible .ai or .ait opened from its pages had an empty
File Info. Nothing read the file's document information
dictionary or its XMP packet, so file.info reported no author,
description or keywords, even for a PDF that VectorCraft exported.

vectorcraft_pdf::file_info reads both. The author, description,
keywords, copyright status, notice and URL and the created and modified
dates come from the XMP packet (dc:creator, dc:description, dc:subject
or pdf:Keywords, xmpRights:Marked, dc:rights, xmpRights:WebStatement,
xmp:CreateDate, xmp:ModifyDate), else from the dictionary's Author,
Subject, Keywords, CreationDate and ModDate. document.open,
file.newFromTemplate and file.revert put each value through file.info's
checks and leave out a value that file.info refuses. A packet that is
not well-formed XML leaves the dictionary's values. File Info does not
set the title, and a file restored from the document it carries keeps
that document's File Info. vectorcraft-pdf depends on roxmltree 0.21,
which usvg already brings into Cargo.lock.

Docs: docs/mcp.md (File Info), docs/file-format-parity.md,
docs/target-app-parity.md.
2026-10-16 06:16:34 +07:00
dotCooCoo d1089270ba Fonts: a removed font that type used leaves the list at the next font scan
When type had used a font, removing the font while the app ran left
it in the font menus, and the type kept drawing in it until the app
restarted. The font list is the loaded faces plus the scanned catalog,
and a scan replaced the catalog but kept every loaded face.

A scan now drops each loaded face whose file it did not catalog, with
the face's cached outlines. A family with no face left leaves the font
lists, and type set in it lays out in the fallback font and is
reported missing, as when a document opens without the font. When the
font is installed again, the next scan lists it and the type draws in
it again. Bundled fonts and fonts added as bytes have no file and
stay.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 06:16:34 +07:00
bflatastic 08dde8783e Actions: Batch… stays in the Actions panel menu, as the reference app has it (#1938) 2026-10-16 04:55:01 +07:00
Fabio William ConceiçãoandCursor 0fe70314b8 G6: action.batch for File › Automate › Batch
Play an action on every file in a folder or every data set, with destination
and Override Open/Save/Export. Actions panel and File › Automate › Batch…
open the dialog; new strings in pt-br.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 04:50:07 +07:00
bflatastic 568008dd47 Variables: graph values go through the dataset CSV too (#1941) 2026-10-16 04:50:07 +07:00
Fabio William ConceiçãoandCursor dc390aba37 G6: graph variables (CSV data merge)
VariableKind::Graph binds a graph root; dataset rows supply CSV as graph.setData.
Make Graph Dynamic, Variables dialog, capture/apply, and pt-br for the new strings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 04:50:07 +07:00
bflatastic 192a671d4f Variables: dataset CSV keeps line breaks in quoted cells and reads a file only up to its cap (#1929) 2026-10-16 04:50:07 +07:00
Fabio William Conceição 5ef090a0c2 G6: dataset CSV import/export with PT-BR UI labels
Load and save variable datasets as CSV for data-merge pipelines, and
translate the new Variables panel commands in Brazilian Portuguese.

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	docs/target-app-parity.md
2026-10-16 04:50:07 +07:00
bflatastic a38c824066 Variables: an image variable keeps the object's bounds with the Placement Options API on main (#1908) 2026-10-16 04:50:07 +07:00
Fabio William Conceição 41c8bd2a2d G6: image variables swap linked files and keep bounds
Add VariableKind::Image with path values on datasets, makeImageDynamic,
panel/dialog UI, and MCP docs so data merge can drive placed images.
2026-10-16 04:45:06 +07:00
Fabio William ConceiçãoandCursor 80090e7623 G5: Symbol Options Registration 9-point grid
Store the registration point with the symbol and place instances so that point lands on (x, y). Symbol Options exposes the same 9-point control as Transform.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-16 04:45:03 +07:00
sanyamsingh001andCodex 848d902354 G1: Add Warp Options controls for live effects
Expose existing Warp styles, orientation and percentage controls through the shared live effect dialog while preserving preview history and effect targets. Keep ordinary Enter owned by focused non-searchable combo controls.

Co-authored-by: Codex <noreply@openai.com>
2026-10-16 04:45:03 +07:00
Fabio William Conceição ed6d474042 G11: Asset Export seam regression and supersampled thumbs
assets.export of abutting fills must leave no ghost edge (#983 / #1037); panel thumbnails use the same Art Optimized supersample. Progress docs mark the gap closed.
2026-10-16 04:45:01 +07:00
sanyamsingh001andCodex 67f9d134ed G11: Preserve gaps when deleting open-path anchors
Keep surviving drawable runs separate when Delete or Backspace removes an open-path anchor. Preserve appearance, selection and one-step Undo.

Co-authored-by: Codex <noreply@openai.com>
2026-10-16 04:44:58 +07:00
Gert Pellin 3ae6cf28e4 G13: Intertwine tool, Object › Intertwine › Edit, menu and translations (#1763)
Make and Edit choose the Intertwine tool: hovering highlights the overlap
area under the pointer, a click swaps which object is on top there, and a
path drawn around overlaps (Shift: a rectangle) arranges every overlap
inside it; Esc or Enter goes back to the tool chosen before. The overlap
faces move to vectorcraft-tools, shared by the tool and the command.

Clicks inside a raised area pick the member drawn on top there, the
Object menu gets the Intertwine submenu, and the new labels and the
predicate message are translated in the complete catalogs. docs/gaps.md
records what landed under G13.
2026-10-16 04:44:53 +07:00
Gert Pellin 74c391c7ba G13: Object › Intertwine: Make, Release and arranging overlap areas (#1763)
An Intertwine group keeps its objects as they are, in their own stacking
order; each member can be drawn over the others in an area of its own.
The group draws, hits, saves and exports through its live expansion, so the
members stay editable and Release gives them back unchanged.

object.intertwine.arrange picks an overlap area by a click point or by a
closed path drawn around overlaps and toggles which member is on top
there, or brings a member to the front or sends it to the back where three
or more objects overlap.
2026-10-16 04:44:52 +07:00
Gert Pellin 736abd811d paint.setFill / setStroke: span "selection" places one gradient across the targets (#2127)
A gradient given to several objects fits each one's own bounds. With
span: "selection", a linear or radial gradient without its own vector is
placed once on the union of the targets' bounds, at its angle and aspect,
and every target gets that vector in document coordinates, so the ramp
runs on across them. A solid colour, a swatch, a freeform gradient or an
explicit start/end with it is refused before anything changes; the next
object drawn still fits the gradient to itself.
2026-10-16 04:44:50 +07:00
Gert Pellin 265552d5f4 Tests: a gradient spanning the selection (#2127) 2026-10-16 04:44:50 +07:00
Gert Pellin e7aacec852 Selection-only commands take ids: the objects are selected first (#2130)
object.group, ungroup, the clipping mask and compound path Make/Release,
the Arrange commands, lock, hide, edit.duplicate, object.move, rotate,
scale, reflect, shear and the Pathfinder commands acted only on the
selection, so a script had to send select.set first. Given ids, they now
select those objects (select.set, with its checks) and run as on the
selection, in the same undo step; without ids nothing changes.
2026-10-16 04:44:50 +07:00
Marco Arnulfo 32dbc4b641 PDF export: the output intent shares the stream of the profile that tags the colours (fixes #1954) 2026-10-16 04:44:31 +07:00