Commit Graph
4739 Commits
Author SHA1 Message Date
BFlat 441c543a66 Merge pull request #3336 from dotCooCoo/fix/issue-1816
Part of #1816: Snap to Grid lands a moved object by its nearest edge and no longer snaps its center
2026-10-21 17:32:01 +07:00
BFlat de6f153032 Merge pull request #3306 from HEOJUNFO/fix/path-only-images
Pick placed images on their frame with Selection by Path Only
2026-10-21 17:31:47 +07:00
BFlat 84c21707b9 Merge pull request #2682 from dotCooCoo/fix/engine-language-test-runs-japanese
Tests: the engine-language test runs a Japanese frame again
2026-10-21 17:31:30 +07:00
BFlat b94311c97b Merge pull request #2672 from dotCooCoo/fix/svg-live-text-shrink-to-fit
Export: area type that Shrink Text to Fit scales down is written at the size it draws at
2026-10-21 17:31:14 +07:00
BFlat 3854155f18 Merge pull request #2671 from dotCooCoo/fix/isolate-refuses-locked-art
Isolation mode: object.isolate refuses hidden or locked objects and locked guide paths
2026-10-21 17:30:56 +07:00
BFlat 27a6993a8d Merge pull request #2596 from dotCooCoo/fix/bg-uk-blend-spine-term
i18n: each catalog names the blend spine with one term
2026-10-21 17:30:40 +07:00
BFlat 747d7096fb Merge pull request #3207 from Taha-Mahmoodi/chore/wasm-warnings-dup-check
Build: the wasm check is warning-free; one file-dialog check instead of two
2026-10-21 17:30:24 +07:00
BFlat 36338753af Merge pull request #3355 from dotCooCoo/feat/new-layer-options-key
Layers: Cmd+Alt+L opens Layer Options for a new layer
2026-10-21 17:30:05 +07:00
BFlat c299686326 Merge pull request #3351 from dotCooCoo/feat/blob-brush-shift
Blob Brush: Shift keeps the stroke horizontal or vertical
2026-10-21 17:29:51 +07:00
BFlat 36d7f3b3b6 Merge pull request #3348 from MarekSpodBiedry/issue-3309
Draw from centre (Alt) now applies to the typed size of a rectangle or ellipse (#3309)
2026-10-21 17:29:33 +07:00
BFlat b597b5bbe0 Merge pull request #3310 from dotCooCoo/feat/optical-margin-type-on-a-path
Type: Optical Margin Alignment hangs punctuation at the ends of type on a path
2026-10-21 17:29:16 +07:00
BFlat 5973245909 Merge pull request #3288 from dotCooCoo/feat/kashida
Paragraph: Insert Kashidas justifies Arabic lines with kashidas
2026-10-21 17:29:01 +07:00
BFlat 294ba818a7 Merge pull request #3295 from dotCooCoo/feat/every-line-mixed-sizes
Type: the Every-line Composer composes paragraphs that mix type sizes in rectangular area type
2026-10-21 17:28:46 +07:00
BFlat 254fb1605b Merge pull request #3287 from dotCooCoo/fix/undo-groups-close-only-their-document
Undo groups: library styles and field scrubs close only the group they open
2026-10-21 17:28:31 +07:00
BFlat e013061c9a Merge pull request #3294 from dotCooCoo/fix/undo-group-cancel-cuts-the-earliest-journal-entry
Undo groups: a cancel drops only the canceled groups' entries from the journal
2026-10-21 17:28:16 +07:00
BFlat 1f73cdf7bc Merge pull request #3293 from dotCooCoo/fix/eps-errors-explain-postscript-error-names
EPS import: a PostScript error shows what its name means in the interface language
2026-10-21 17:28:00 +07:00
BFlat 06cf9d5dea Merge pull request #3311 from dotCooCoo/feat/graph-column-design-totals
Graphs: column designs show each column's value where their type has % and two digits
2026-10-21 17:27:45 +07:00
BFlat dff3a18584 Merge pull request #3318 from dotCooCoo/feat/tabs-panel-vertical-type-labels
Tabs: in vertical type the alignments read Top- and Bottom-Justified Tab and the position Y
2026-10-21 17:27:30 +07:00
BFlat 77c58db7b6 Merge pull request #3322 from dotCooCoo/fix/objects-on-path-refuses-live-containers
Objects on Path: clipping paths and parts of blends, envelopes and repeats stay in place
2026-10-21 17:27:14 +07:00
BFlat e84073e535 Merge pull request #3320 from dotCooCoo/fix/color-panel-new-swatch-keeps-the-color-model
Swatches: a swatch made from a color the app shows keeps that color's model in a CMYK document
2026-10-21 17:26:58 +07:00
BFlat 10eba8be69 Merge pull request #3327 from dotCooCoo/fix/live-paint-merge-keeps-the-group
Live Paint: Merge keeps the group's lock, visibility, mask and graphic style link
2026-10-21 17:26:42 +07:00
BFlat e407202107 Merge pull request #3305 from dotCooCoo/fix/live-paint-click-selects-hovered-edge
Live Paint: a Live Paint Selection click outside the faces selects the edge it highlights
2026-10-21 17:26:25 +07:00
BFlat 04ca2f066d Merge pull request #3177 from lehni/refactor/file-pick-filters
UI: FilePick filters own their text, so a dialog's file types can be made at run time
2026-10-21 17:26:09 +07:00
bflatastic 30203e60a1 PDF/.ai import: a .ai saved without PDF compatibility is placed and pasted from its editing data
Squashed from #2182 onto main.
2026-10-21 17:11:08 +07:00
dotCooCoo a5ea3a8002 Use Preview Bounds: a handle drag keeps the opposite visible edge in place
With Use Preview Bounds on, a bounding-box handle drag scaled the art by
the ratio of the visual box, so margins that stay (a stroke with Scale
Strokes & Effects off, an Offset Path reach) or scale by another factor
moved the opposite visible edge: dragging the right edge of a square with
Offset Path 10 from 210 to 310 left its left edge at 98.33 instead of 90.

The Selection tool now previews object.setBounds with the dragged visual
size and the opposite handle (with Alt, the center) as the reference
point, so the scale is solved from the geometric box and the margins the
visual box adds, for strokes with Scale Strokes & Effects on or off and
for geometry effects. Shift and Alt drags follow the same rule; a drag
that flips the box, and one with anchors selected, still scale by the
matrix. A previewed object.setBounds runs as object.transform with the
transform it finds, so Transform Again repeats the drag and the journal
holds that transform. The drag transforms a path of a compound path
selected on its own, as object.transform does, and leaves the art at
least 1e-4 of its width and height.
2026-10-21 17:11:08 +07:00
bflatastic dda52d0b20 Artboards: each artboard can have a background color on the canvas
Squashed from #2403 onto main.
2026-10-21 17:11:08 +07:00
dotCooCoo 9acfd1ea8a Web: a pan or zoom of a heavy document moves the last frame and renders once the view is still
The web build has no render worker, so every frame of a pan, zoom or
rotation of the view rendered the whole canvas on the browser's main
thread before it was shown. In Chrome, each of the 20 steps of a
Space-drag over a document of 50,000 paths took a frame of 533 to
900 ms.

In the web build the canvas now waits while only the view changes on
a document whose last render took more than 8 ms. It draws the last
frame moved, scaled or turned with the view, as the desktop app does
while its worker renders, and it renders once the view has been still
for 0.2 s. The same Space-drag now keeps every frame within 17 ms and
renders once, in 667 to 767 ms, after the view stops. After an edit, a
preview or an option change the canvas renders at once. The desktop
app renders as before, with or without its worker.

Docs: docs/target-app-parity.md.
2026-10-21 17:11:08 +07:00
dotCooCoo f009f4f3e5 Part of #2384: imported PDF text keeps its place when the file draws the space beside a wide gap
PDF import gives a space read from a gap between two glyphs tracking of
its own when the line's tracking cannot place the glyphs after it. A
space glyph that the file draws was not measured this way: the gap
before or after it was spread over the whole line as one tracking
value, so the words after it moved. VectorCraft's own PDF export draws
a tracked space and leaves its tracking after it.

A space the file draws between two glyphs on a line is now measured
from the end of the glyph before it to the start of the glyph after it,
and takes tracking of its own on the same terms as a space read from a
gap. Two or more spaces in a row, and a space at the start or end of a
line, keep the line's tracking. The tracking calculation builds a map
of the line's laid-out advances by byte offset once.

A wrapped line whose spaces each have tracking of their own, within a
fiftieth of an em of the first space's tracking, can still come back as
part of justified area type. Wrapped lines whose spaces' trackings
differ by more than that stay point type with their words in place.
2026-10-21 17:11:08 +07:00
dotCooCoo 1adca4f484 i18n: Georgian rows for Shape Builder Tool Options
Georgian is a complete-menu catalog on main, so the dialog's seven new
labels need rows in ka.tsv.
2026-10-21 17:11:08 +07:00
dotCooCoo d532e6d341 Use Preview Bounds: a clip group is measured where its clipping path's stroke paints
The renderer clips a clip group by its clipping path's geometry before
the path's geometry effects, and paints the path's strokes over the
clipped art without clipping them, with those effects. With Offset Path
on a stroked clipping path, the stroke paints along the offset shape
while the group was measured by the path's own stroke.

With Use Preview Bounds on, a clip group or layer whose clipping path
has strokes with geometry effects is now measured as the clipping
path's geometry and those strokes as drawn, in the Transform panel, the
bounding box, Align and Distribute. This holds inside groups and for a
clip group with a fill or stroke of its own. A clipping path
without a stroke still measures as the region it clips to, and a hidden
one adds no stroke.
2026-10-21 17:11:08 +07:00
dotCooCoo 1d2f50ec71 i18n: Dutch, Danish and Indonesian rows for Shape Builder Tool Options
Dutch, Danish and Indonesian are complete-menu catalogs on main, so the
dialog's seven new labels need rows in nl.tsv, da.tsv and id.tsv.
2026-10-21 17:11:08 +07:00
dotCooCoo 9f31471042 Use Preview Bounds: the selection tools measure the selection once per revision
With Use Preview Bounds on, every hover frame's cursor and every
pointer move measured the selection again, and with Smart Guides on
each selected object too, evaluating their geometry effects each time.

The session now keeps a PreviewMemo, which ToolContext::preview_memo
passes to the tools. selection_bounds, selection_box and the object
bounds that Smart Guides find centers in while hovering read it. The
memo holds the measurements for one key: the document's uid and
revision, the selected objects and Use Preview Bounds. A new key
replaces them. VectorcraftApp::selection_box in the UI keys on the
uid, the revision and Use Preview Bounds. A ToolContext without a
memo measures on every call.
2026-10-21 17:11:08 +07:00
dotCooCoo 1267efbcf9 Use Preview Bounds: objects are measured with their geometry effects
With Use Preview Bounds on, the bounding box, the Transform panel,
Align, Distribute, Distribute Spacing and object.setBounds measured
each object without its geometry effects. A square with Offset Path
10 measured as the square, and type with Outline Object measured by
its layout box instead of its glyphs.

The new vectorcraft_effects::preview module measures objects as they
are drawn. A path is measured by its effected shape, with the
geometry effects of each fill and stroke that paints. Type, images,
live objects and symbol instances are measured by their reshaped art.
A group or layer with geometry or Pathfinder effects, or with a fill
or stroke of its own that has geometry effects or paints members with
geometry effects, is measured by its evaluated art. A blend, envelope
or repeat whose objects have geometry effects is measured by its
evaluated art, with those objects measured after their effects. Raster
effects are not measured. In one object, the art of at most 4,096 symbol
instances is measured with its geometry effects, and the other
instances are measured without them. With Use Preview Bounds off, the
measures are unchanged.

object.setBounds sizes objects with geometry effects by measuring
them after each scale it tries, so type with Outline Object set to
100 pt wide gets a glyph box 100 pt wide.

Selection::anchors_box is split out of Selection::anchor_bounds. The
doc comment of VectorcraftApp::selection_bounds said that it includes
effects; it now says that it leaves them out.
2026-10-21 17:11:08 +07:00
dotCooCoo e5b1f0c191 Shape Builder: double-clicking the tool opens Shape Builder Tool Options
Double-clicking the Shape Builder tool reported that the tool has no
options. The reference app opens Shape Builder Tool Options there.

The dialog sets Consider Open Filled Paths as Closed, Pick Color From
(Artwork or Color Swatches), Cursor Swatch Preview, Selection (Freeform
or Straight Line) and Highlight Fill. The defaults keep today's
behavior. With Consider Open Filled Paths as Closed off, a filled open
path cuts regions as an unfilled one does. With Color Swatches, a merge
fills the new shape with the current fill, and the Left and Right arrow
keys step the current fill through the document's color swatches.
Cursor Swatch Preview shows the current fill and the swatches before
and after it above the pointer. With Straight Line, a drag merges what
the line from its first point to its last crosses. With Highlight Fill
off, merge mode shades no regions.

The options persist like other tools' options. shapeBuilder.merge
takes openFilledAsClosed and pickColorFrom, and shapeBuilder.regions
takes openFilledAsClosed. Gap Detection, "In Merge Mode, Clicking
Stroke Splits the Path" and Highlight Stroke When Editable are not part
of this change.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-21 17:11:07 +07:00
dotCooCoo 6f95f1cf09 i18n: Dutch, Danish and Indonesian rows for the Artboard tool's Shift hint
main now lists nl, da and id as complete-menu catalogs, so the two hint
fragments this branch adds need rows there too. Each row replaces the
old " on the canvas to create one" row, as in the other catalogs.
2026-10-21 17:11:06 +07:00
00xJ0 096544db40 Test: Press Quality keeps a CMYK image's inks in an RGB document (#3012 review)
The colour space alone could pass a conversion that changed the inks: the exported image is
opened again and its inks compared with the ones placed.
2026-10-21 16:55:17 +07:00
Ayberk A. 0d4b597983 AI import: a gradient stop naming a process colour leaves its swatch process (#1771)
A colour named by a gradient stop (`Bs`) was collected with the colours the
colour operators define, in file order, and the first definition of a name
made its swatch. A stop can't say whether its colour is an ink, so it reads
as a spot colour; and since the file defines its gradients before the art,
a global process colour set with `Xk` that a gradient also names came in as
a spot swatch, which PDF export then writes as a Separation. The stops'
named colours are now kept apart and make swatches only after the colour
operators' colours, so `Xk`'s definition wins and a name only a stop uses
still gets its swatch. The stop's values were already read right since the
lexer reads a `%_BS` line's own operands.
2026-10-21 16:55:17 +07:00
dotCooCoo 37a8aa4946 Canvas: the pointer's document point follows a wheel scroll or view drag in the same frame
handle_input set hover_doc, the document point under the pointer,
from the view as handle_input found it. The wheel's zoom, scroll and
trackpad turn, a held pan, a Rotate View drag, the Zoom tool and a
Presentation Mode click moved the view later in the same call, and
show drew with the moved view. In a frame where a wheel scroll moved
the view, the rulers' pointer marks were drawn off the pointer by that
frame's scroll, the Info panel showed the earlier point, and the hover
highlight and the tool cursor were looked up there. A pan, a Rotate
View drag, a Zoom tool marquee and an Animated Zoom scrub left
hover_doc behind the view in the same way. A tool drag held still
while the wheel scrolled was sent the old view's point.

The wheel block now runs before handle_input takes its view, so the
tool's events in that frame use the view the wheel moved to. show sets
hover_doc right after handle_input returns, from the view it left.
2026-10-21 16:55:17 +07:00
dotCooCoo f761ae2585 Pencil and Paintbrush: holding Alt shows the closing loop cursor
Holding Alt while drawing with the Paintbrush, or with the Pencil while
Alt key toggles to Smooth Tool is off, closes the path at the release,
but the cursor did not change. The reference app's Paintbrush shows a
small loop while Alt is held, and its Pencil shows a small circle.

Cursor::PaintbrushClose and Cursor::PencilClose show while Alt is held
with the Paintbrush, and with the Pencil while that option is off,
before the press and while drawing. Both tools still borrow the
crosshair, so the closing cursors are drawn as that crosshair with the
PenClose ring. Use Precise Cursors turns them into the plain crosshair,
as it does PenClose.

With Alt key toggles to Smooth Tool on, the Pencil shows the Smooth
cursor while Alt would smooth: with Alt held before a stroke begins to
draw, and through a smoothing drag even after Alt is let go. Before, it
showed the Smooth cursor whenever Alt was held, also during a stroke
that kept drawing, and not during a smoothing drag once Alt was let go.

Docs: docs/target-app-parity.md, crates/tools/src/cursors.toml.
2026-10-21 16:55:17 +07:00
dotCooCoo bcf8732d67 Artboards: Rename gives several selected artboards one name, numbered
With several artboards selected with the Artboard tool, the Artboards
panel menu's Rename opened the name field on the active artboard's row
and renamed that artboard only.

Rename now opens an empty name field on the active artboard's row, and
so does a double-click on a name with Shift or Ctrl/Cmd held that
leaves several artboards selected. The name typed there goes to every
selected artboard, numbered in panel order (abc-1, abc-2, abc-3), in
one undo step through the artboards list of artboard.setProps. With
one artboard selected, Rename and a double-click on a name rename that
artboard as before.

Docs: docs/target-app-parity.md.
2026-10-21 16:55:17 +07:00
dotCooCoo 2c4d0bac92 Brushes: colorization recolors type in scatter brush art
A scatter brush whose art held type drew the type in its own colors
whatever the brush's colorization: Tints, Tints and Shades and Hue
Shift recolored the art's paths but not its characters. The Key Color
list in Brush Options left the type's colors out as well.

Colorization now maps the solid fills and strokes of each run of type
in the art as it maps the fills and strokes of paths. The Key Color
list, whose first color is Hue Shift's default key, counts each run's
fill, and its stroke when the stroke is wider than 0. The Symbol
Stainer now mixes the characters' strokes of type toward the stain,
as it already mixed their fills.

Docs: docs/target-app-parity.md.
2026-10-21 16:55:17 +07:00
dotCooCoo 6a1c8eda46 Part of #1816: Snap to Grid lands a moved object by its nearest edge and no longer snaps its center
With View › Snap to Grid on, a whole object moved with the Selection,
Group Selection or Direct Selection tool landed on the grid by
whichever of its two edges or its center was nearest a gridline on
each axis. An object on the grid whose width is an odd number of grid
steps has its center half a step between gridlines, so a short drag
could pull it half a step off the grid: on a 20 pt grid, a 100 pt
square at (100, 100) dragged by (8, 8) moved by (10, 10).

A move now lands the nearer of the two edges on a gridline on each
axis, as the reference app's help describes, and the center no longer
snaps. The square above stays at (100, 100). This changes the rule #769
chose for #740. The plain move and the Shift-constrained one both use
grid_pull, so both change.

Docs: docs/target-app-parity.md (Drawing aids).
2026-10-21 16:55:17 +07:00
HEOJUNFOandClaude Opus 5.5 764d04926e Pick placed images on their frame with Selection by Path Only (#3291)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 16:55:17 +07:00
dotCooCoo 1047c82bd3 Artboard tool: Shift-drag draws an artboard inside another or marquee-selects artboards
With the Artboard tool, a Shift-press on an artboard toggled it in the
selection and started a move, which Shift kept at 45° steps. A
Shift-drag on the pasteboard drew a square artboard. The reference
app's documentation gives Shift-drag inside an artboard as the way to
draw an artboard there, and Shift-drag on the canvas as a marquee that
selects artboards.

A Shift-click now toggles its artboard when the button comes up. A
Shift-drag inside an artboard draws a new artboard (artboard.new), and
a Shift-drag on the pasteboard toggles every artboard it touches, the
last one added becoming active. Shift no longer constrains an artboard
move or keeps a new artboard square, and no other modifier does. Shift
on the active artboard's handles still resizes it proportionally. The
hint bar names the Shift gestures in every complete catalog, and the
cursor is a crosshair over an artboard while Shift is held.

Escape while a new artboard was being drawn left the tool. Escape
during any drag, the new marquee included, now cancels the drag and
keeps the Artboard tool, as it already did during a move or a resize.

Docs: docs/target-app-parity.md.
2026-10-21 16:55:17 +07:00
00xJ0 91107c89d2 PDF: a Document CMYK destination; Press Quality converts to it in RGB documents too (#3012 review)
A blank destination is the document's own profile, so in an RGB document Press Quality (Preserve
Numbers, blank) converted CMYK colours and images to RGB. Output › Destination can now be
DOCUMENT_CMYK, the CMYK profile of the colour settings in effect whatever the document's colour
mode (Illustrator's "Document CMYK"); Press Quality uses it, and the Save PDF dialog lists it after
Document profile. Test: in an RGB document a CMYK image keeps its inks and an RGB image comes out
CMYK.
2026-10-21 16:55:17 +07:00
00xJ0 5d0b966548 PDF presets: Press Quality converts to the document's profile, keeping its numbers (#2950)
Press Quality left Output at No conversion, so in a CMYK document RGB images went to the press as
DeviceRGB. It now converts to the destination with the numbers kept, the destination blank (the
document's profile): RGB comes out CMYK in a CMYK document, CMYK values stay exactly, RGB
documents are unchanged, and no profiles are included, as a printer's published [Press Quality]
settings list.
2026-10-21 16:55:17 +07:00
dotCooCoo 3cd2ae2f12 Tests: the engine-language test runs a Japanese frame again
the_engine_follows_the_interface_language drew only English frames
after 6efe9df3e, because a Japanese frame set the drawing language for
every test running beside it. It set the engine's language by hand and
never checked that a Japanese frame hands the engine ja. Since #2570
each unit-test thread has its own drawing language.

The test again runs a frame with the interface preference set to
Japanese and checks that the engine gets ja, then runs a frame with
the preference back on auto and checks that it gets en. It also checks
that a thread started after the Japanese frame still draws in English.
With the drawing language process-wide, as before #2570, the test
fails, and the zoom hint test beside it showed its hint in Japanese in
12 of 200 runs.
2026-10-21 16:55:16 +07:00
dotCooCoo 68d8a9c755 Export: area type that Shrink Text to Fit scales down is written at the size it draws at
Shrink Text to Fit scales area type down at layout time, and the canvas,
PDF export and the exports that write type as outlines draw it at that
size. SVG export wrote the live text at its stored font size, letter
spacing and baseline shift, so a viewer drew it larger than the canvas
does. Legacy .ai saves wrote the stored size, leading and baseline shift
into the text objects, DXF export took the TEXT heights from the stored
size, and the CSS Properties panel gave the stored font size, letter
spacing and line height.

The text crate's scaled, which the layout scales shrunk text with, is
now public. SVG export, legacy .ai saves and the CSS Properties panel
take the type's styles from it at the layout's fit scale, and DXF
export multiplies the TEXT heights by that scale. Positions and
textLength in SVG still come from the layout.

Docs: docs/target-app-parity.md.
2026-10-21 16:55:16 +07:00
dotCooCoo b1abc73e16 Isolation mode: object.isolate checks a Shaper Group's source group
object.isolate on a Shaper Group isolates the group's source group, but
the hidden and locked check ran on the Shaper Group itself, so a source
group hidden or locked with object.setProps was still isolated. The
check now runs on the object that isolation mode enters.
2026-10-21 16:55:16 +07:00
dotCooCoo bdf54007a0 Isolation mode: object.isolate refuses hidden or locked objects and locked guide paths
A click with the Selection tool does not select a hidden or locked
object, an object in a hidden or locked group or layer, or a guide path
while Lock Guides is on, but object.isolate isolated each of them. Once
a locked or hidden group, or a group on a locked or hidden layer, was
isolated, a Selection tool drag moved the member under the pointer. The
Layers panel's Enter Isolation Mode sent the same command for a locked
or hidden layer or group.

object.isolate now refuses a hidden or locked object, or one in a
hidden or locked group or layer, with "the object is hidden or locked"
(the text of layer.target), and a guide path while Lock Guides is on
with "guides are locked". Isolation mode stays off, and the selection
stays as it is.

Docs: docs/mcp.md.
2026-10-21 16:46:57 +07:00