Commit Graph
213 Commits
Author SHA1 Message Date
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
bflatastic 255ce30103 Docs: one ranked-table row each for G2 and G13 in gaps.md 2026-10-24 03:31:54 +07:00
bflatastic 6e3ecc3adf Select: Start Global Edit edits the objects of the same shape together, each in proportion
Squashed from #3781 onto main.
2026-10-24 03:09:12 +07:00
Taha-Mahmoodi 7da5e3062d Symbols: Window › Symbol Libraries opens symbol libraries whose symbols click into the document's Symbols
Window › Symbol Libraries and the Symbols panel's Symbol Libraries Menu open each symbol library
in a library panel of its own, on the shared library panel code the swatch and graphic style
libraries use (https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/create-or-import-symbol-libraries.html,
.../symbols-panel-options.html, .../create-and-place-symbols.html).

- Basic Shapes: one original library generated in code (circle, squares, triangle, diamond,
  hexagon, star, burst, arrow); no other app's libraries.
- A click adds a library symbol to the document's Symbols as one undo step, with its Symbol
  Options and the resources it uses (nested symbols, patterns, swatches, brushes, graphic styles,
  images), and makes it the current symbol; one already there isn't added again, a clash of
  names numbers the library's. Shift/Cmd-clicks select for Add to Symbols. Dragging a library
  symbol onto the canvas adds it and places an instance there, in the same step.
- Save Symbols… (library menu, Window menu) and Save Symbol Library… (Symbols panel menu) write
  the document's symbols as a .vectorcraft document holding only them, into the user library
  folder (User Defined, window.userSymbolLibrary1-10) or a file; Other Library… reads the symbols
  of any document VectorCraft opens, and a file it can't read is an error.

Commands: symbol.libraries, symbol.library, symbol.addFromLibrary (place, x, y),
symbol.saveLibrary, symbol.loadLibrary; UI: window.symbolLibrary (.other), ui.saveSymbolLibrary
(dialog saveSymbolLibrary). i18n rows in every complete catalog; docs updated.
2026-10-24 03:09:10 +07:00
bflatastic f247772042 Type: selected type shows its in and out ports, and the in port threads backwards
Squashed from #3817 onto main.
2026-10-24 03:09:10 +07:00
Taha-Mahmoodi a301760aa6 Smart Guides: Alt over another object or the artboard shows the selection's distances to it
Distance Guides, from the public "Use Distance Guides for accurate
placement" and "Smart Guides options" (Invoke Distance Guides) pages:
with Smart Guides on, something selected and the Selection tool,
holding Alt/Option over another object shows the horizontal and
vertical distances between the selection and it, and over an empty
part of an artboard the selection's distances from the artboard's left,
right, top and bottom edges.

- guides::distance_guides measures the selection's bounds to a box: on
  an axis on which they lie apart, the gap between their facing edges;
  otherwise the offsets of both pairs of matching edges. Each line runs
  across the middle of the overlap on the other axis (else the
  selection's middle), with a dashed line out to the target's edge when
  it passes beside it. An artboard is always measured edge to edge.
  Labels are in the General unit, in the smart guide colour.
- The Selection tool keeps what its Alt hover points at and draws the
  guides while idle; letting Alt go (the canvas re-hovers on a modifier
  change), a press or moving off clears them.
- Overlay::Distance: the line with end ticks and its length in a pill;
  the canvas draws it.
- Preferences › Smart Guides › Invoke Distance Guides (`distanceGuides`,
  on by default), with i18n rows; View › Smart Guides gates it too.
- Hover only, as the pages describe: nothing while moving or nudging.

Tests: select::tests::alt_over_* (gap and edges beside and below,
artboard edges in pt and mm), distance_guides_need_alt_a_selection_and_
smart_guides, tests_prefs::invoke_distance_guides_measures_the_selection_
to_the_object_under_alt, canvas::tests::alt_over_another_object_draws_
the_distance_guide_label (frame test of the drawn label and line).
2026-10-24 03:09:09 +07:00
Taha-Mahmoodi e7f2ab9a8e Docs: Pathfinder › Trap ships; Effect › Pathfinder 13/13, Illustrator effects 49/50 2026-10-24 03:09:08 +07:00
bflatastic 422ecc0c77 Artboards: Artboard Options shows video safe areas and sets the pixel aspect ratio that View › Rulers › Show Video Rulers counts the artboard in
Squashed from #3831 onto main.
2026-10-24 03:09:07 +07:00
Taha-Mahmoodi 7c8840b93a Tools: double-clicking the Line Segment, Arc, Rectangular Grid and Polar Grid tools opens their Tool Options
Step 3 of the Rectangular Grid tool and Arc tool help pages
(https://helpx.adobe.com/illustrator/using/tool-techniques/rectangular-grid-tool.html,
https://helpx.adobe.com/illustrator/using/tool-techniques/arc-tool.html)
and the Tools page (https://helpx.adobe.com/illustrator/using/tools.html)
say a double-click on the tool opens its Tool Options. For these four tools
the double-click (tool.options, and Enter once #2891 lands) said the tool
had no options (#2691).

The Tool Options are the tool's click dialog in dialogs/shapes.rs, opened
without a place to draw (shapes::open_options; a `__toolOptions` field
marks it), as the review of #2979 asked: no copied form code. Its OK runs
tool.setOption and draws nothing; Cancel changes nothing. The grids' dialogs
are titled Rectangular Grid / Polar Grid Tool Options there. The options
are what the next drag draws with and what a click's dialog opens at:
- Line Segment: Length and Angle (now kept by the tool and used by the
  click dialog) and Fill Line (new: shape.line {fill: true} gives the line
  the current fill; the click dialog has it too);
- Arc: lengths, Reference Point, Type, Base Along, Slope, Fill Arc;
- the grids: dividers, skews and switches, plus the Default Size and
  Reference Point, now kept by the tool (width, height, reference) for the
  click dialog; a drag leaves them out of its command.
The grids' Reference Point is drawn only as the 9-point locator (it also
showed as a plain number field).

New strings (Rectangular Grid Tool Options, Polar Grid Tool Options, Fill
Line) have rows in all 24 catalogs.

Verification:
toolbar::tests::double_clicking_the_line_arc_and_grid_tools_opens_their_tool_options
double-clicks each tool's toolbar button: the dialog and its title, Cancel
leaves the next drag as it was, OK draws nothing, the next drag uses the
option and a click's dialog opens at it (a grid at its Default Size);
line_segment_options_and_fill_line, grid_default_size_is_an_option_the_drag_leaves_out,
fill_line_gives_a_new_line_the_current_fill. Grid dialog tests now expect
the stored Default Size. Live: double-clicking the Rectangular Grid tool in
the release build opens Rectangular Grid Tool Options; OK with 2 and 3
dividers, then a drag, draws a 3 × 4 grid.
2026-10-24 03:09:07 +07:00
dotCooCoo 8fa918b30b Pen tablets: a pen's eraser end selects the Eraser on Linux under X11 and XWayland
On Linux under X11, and under XWayland, the X server lists a pen's
eraser end as a pointer device of its own, and winit 0.30 hands it to
the window as a mouse. Turning the pen over and dragging the eraser
end drew with the active tool, for example a Paintbrush stroke.

The tablet reader now marks a pen device whose name or XInput type
contains "eraser", and Services::stylus carries the pen's end with
its pressure. Once a frame while no press is held and the pointer is
in the window, the canvas selects the Eraser when the eraser end
moves, and it selects the tool used before when the tip moves again.
The return goes through Session::switch_tool, so a perspective grid
hidden meanwhile stays hidden. A tool chosen while the eraser end
stands in stays when the tip comes back. The feed starts afresh when
the pen turns over, so a press of one end does not take the pressure
the other end last touched with. Native Wayland is unchanged.

The switch follows PhotoCraft's sync_eraser_tool
(storytold/photocraft#255, and storytold/photocraft#5043 for the
cleared tool).

Docs: docs/development.md, docs/gaps.md, docs/hardware-parity.md,
docs/target-app-parity.md.
2026-10-24 03:09:06 +07:00
Taha-Mahmoodi 52d93cfce5 Font menu: Font Technology › Variable lists only variable font families
As Illustrator's help describes (Filter fonts by classification, Font Technology › Variable), the font menu gets a Font Technology row whose Variable choice lists only the families with variation axes, together with the script, kind, favourites and search filters, and stays set from one opening to the next. A family is variable when any of its faces, loaded or scanned, has an `fvar` table, so a family installed as static files beside a variable one counts.

Spec: https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/find-apply-and-adjust-variable-fonts.html
2026-10-24 02:06:16 +07:00
Jürg Lehni c8fe9193d9 Type: colour glyphs go with type that effects reshape and Expand Appearance
COLR and SVG glyphs, which have no outlines, were left out wherever type was taken as its outlines: geometry effects, Expand Appearance, EMF/WMF export.
2026-10-24 02:06:11 +07:00
Taha-Mahmoodi 309119dc4c Docs: Simplify of part of a path is described in our own words, not the help page's
Review follow-up (CodeRabbit, clean-room): the test header, gaps.md and
target-app-parity.md quoted a phrase from the cited help page. Comments
and docs only.
2026-10-24 02:05:19 +07:00
Taha-Mahmoodi 606796b934 Simplify: with part of a path direct-selected, Object › Path › Simplify refits only the selected runs of segments
The Simplify help page says a path can be simplified whole or "a specific
path region with the Direct Selection tool"
(https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html).
Simplify refitted every selected path whole, whatever part of it was
direct-selected.

object.path.simplify now looks at each path's direct selection: the
segments picked as segments, or else every segment with both anchors
selected. Each run of consecutive selected segments is refitted on its own
(vectorcraft_pathops::simplify_part) between its two end anchors, which stay
where they are with their handles on the side away from the run; every
other anchor and segment stays exactly as it was. A closed path's run may
cross its start point (the path then starts at the run's end anchor),
separate runs on one path simplify independently in one undo step, and the
runs stay direct-selected (as segments when segments were picked). A path
with only lone anchors selected still simplifies whole, as before, and
wholePaths: true simplifies partly selected paths whole. The slider, its
preview, Auto-Simplify and the Simplify dialog all run the command, so
they act on the part too. simplify_with now shares its run fitting with
the part refit (same results).

Verification: crates/engine/src/tests_simplify_part.rs (unselected anchors
and segments equal bit for bit, run end anchors fixed, the run within the
tolerance, two runs equal to each simplified alone in one undo step, a
closed run across the start point, picked segments, wholePaths, slider
preview and Auto-Simplify, a run that can't be simpler records no step)
and simplify_slider_and_dialog_simplify_only_the_direct_selected_part_of_a_path
(the slider's preview, Enter and the dialog's OK). Live: the release build's
slider at level 20 on a 91-anchor wave with anchors 30–60 selected refits
only that stretch.
2026-10-24 02:05:19 +07:00
Taha-Mahmoodi 0a83c19911 Fix #3705: art drawn or pasted inside an isolated object in Draw Inside stays in isolation mode
The drawing modes help page
(https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/learn-drawing-basics/drawing-modes-overview.html)
has you draw or paste into the selected object in Draw Inside. The isolation
help page
(https://helpx.adobe.com/illustrator/desktop/manage-objects/select-objects/isolate-objects.html)
isolates a path, group or object on a double-click with the Selection tool,
and puts art added in isolation mode where the isolated art was once the mode
ends, so that art belongs with what is isolated.

Steps: select a path, choose Draw Inside, double-click the path with the
Selection tool (it is isolated), then draw or paste. The art went into the
clip group Draw Inside makes around the path, but isolation mode stayed on the
bare path, so the new art was outside it: drawn dimmed, passed through by
clicks and marquees, and a double-click on it selected the path and never
entered the clip group. Double-clicking an existing Draw Inside clip group
from outside isolation mode already isolated it, and pastes then went inside
it; that is unchanged.

Cause: create::insert_look_turned and edit::paste_clip put the art into
create::draw_inside_group's clip group but left DocState::isolation on the
object drawn into, and with a single object isolated, hit testing reaches
only that object.

Change: when the object drawn into is the isolated one, isolation moves to its
clip group (create::isolate_draw_inside_group), after new art and after Draw
Inside pastes. A drag's previews replay from its snapshot and make the group
under the same id, and cancelling the drag restores the isolation it began
with. Undo and redo end isolation mode when the isolated object is no longer
in the document (the undone clip group), as mask editing already does,
instead of keeping an id a later object may take.

Verification: drawing_inside_an_isolated_object_keeps_the_new_art_in_isolation_mode
fails without the change (isolation Some(NodeId(2)) instead of the clip group
Some(NodeId(6))). A whole-window egui run through the control channel's
ui.click/ui.drag (double-click the rectangle, drag an ellipse inside, paste,
click the copy) left isolation on the rectangle and the click selected the
rectangle before; after, isolation is on the clip group and the click selects
the copy.
2026-10-24 02:05:19 +07:00
Taha-Mahmoodi eb4635fd4b Fix #3705: pastes go inside the object in Draw Inside, and Shift+D no longer stops at Draw Behind
The drawing-modes help page
(https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/learn-drawing-basics/drawing-modes-overview.html)
has you draw or paste into the selected object in Draw Inside. It also says
the modes apply to Paste in Place and Paste on All Artboards but not to Paste
in Front or Paste in Back.

Cause: only new art (create::insert_look) read the drawing mode. The paste
commands (edit::paste_clip) always used the insertion parent, so a paste in
Draw Inside landed on the layer above the object, outside the clip.
Double-clicking the object first only isolated the bare path, which didn't
change where the paste went. Separately, view.drawMode {} went from Draw
Behind to Draw Inside whenever one object was selected, then refused it unless
the object was a path, compound path or type. So with one group selected
(such as the clip group Draw Inside makes), Shift+D and the toolbar button
failed every time and stayed in Draw Behind.

Change: the clip-group wrapping moves into create::draw_inside_group, which
new art and pastes now share. In Draw Inside, Paste, Paste in Place and Paste
on All Artboards insert the copies at the top of that group, in the paste's
one undo step, ignoring Paste Remembers Layers. In Draw Behind, Paste in Place
and Paste on All Artboards go just below the selection, as Paste in Back does.
Paste in Front, Paste in Back and pasted artboards keep their placement.
Shift+D goes on to Draw Inside only when it can draw into the selection, and
otherwise back to Draw Normal.

Verification: pasting_in_draw_inside_puts_the_copies_inside_the_object,
paste_in_place_in_draw_behind_goes_behind_the_selection and
shift_d_goes_past_draw_behind_whatever_is_selected all fail without the
change ("Layer" != "Clip Group"; index 3 != 0; the Draw Inside error on a
selected group). cargo xtask ci: all 10 steps passed.
2026-10-23 09:56:07 +07:00
Taha-Mahmoodi 64f6d2db50 Artboards: Artboard Options' Show Center Mark and Show Cross Hairs draw on the canvas
The Artboard Options help pages describe two display options
(https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/modify-display-settings-of-artboards.html,
.../set-video-display-options-for-artboards.html): a mark at the artboard's
center, and lines from the middle of each side through the center. The
document already had show_center_mark and show_cross_hairs on Artboard, but
nothing set or drew them.

artboard.setProps (each `artboards` entry too) and artboard.new take
showCenterMark and showCrossHairs as true/false, in the same single undo
step as the other fields; a locked artboard can still change them.
Artboard Options has both checkboxes, filled in from the artboard whether
it opens from the panel or a double-click with the Artboard tool, and its
New Artboard form passes them to artboard.new. The canvas draws them in the
new theme token artboard_mark, 1 point wide on screen at any zoom: a cross
12 points across at the center whose arms follow the artboard's sides, and
the two cross hairs. They are screen-only like the edges; exports and print
never read the fields. inspect_document reports them when on. Saved files
are unchanged: the two keys were already always written, so a document that
never shows them saves byte for byte as before, and a file without the keys
opens with both off. Video safe areas, video rulers and the pixel aspect
ratio are not part of this change.

Verification: artboard_display_marks_are_set_and_cleared_in_one_undo_step,
artboard_display_marks_round_trip_through_the_native_format,
artboard_display_marks_stay_out_of_exports (SVG and PDF identical with the
marks on and off), an_artboard_shows_its_center_mark_and_cross_hairs
(positions, screen-constant size under zoom, rotated view) and
artboard_options_show_center_mark_and_cross_hairs. cargo xtask ci: all 10
steps passed.
2026-10-23 09:42:09 +07:00
Taha-Mahmoodi 08cfb45561 Simplify: Object › Path › Simplify opens its slider at the Auto-Simplify level for the selection
The Simplify help page says the slider starts at an auto-simplified value
(https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/manually-simplify-paths.html).
The slider opened at a fixed level 50 (1 pt), whatever the size of the
selection, so the first preview was the same tolerance for a 30 pt icon as
for a 1000 pt drawing.

The level Auto-Simplify picks for the selection is now one engine function,
vectorcraft_engine::auto_simplify_level (the existing 1/400-of-the-longer-
side rule in vectorcraft_pathops), which object.path.simplify {auto: true}
and the slider's opening fields both use. Object › Path › Simplify (and
ui.menuDialog, the Properties and contextual task bar buttons, which go
through the same menu dialog) opens the slider there, and the canvas
previews that result; Auto-Simplify still moves the slider back to it.
The retained Simplify dialog path is unchanged.

Verification: simplify_slider_opens_at_the_auto_simplify_level_of_the_selection
opens the slider on 1140, 285 and 28.5 pt waves (levels 35, 55, 89) and
checks the preview is exactly object.path.simplify {auto: true}'s result;
without the change it fails with level 50 != 35, and
simplify_opens_a_slider_whose_drag_previews_fewer_or_more_points fails with
{"level": 50} != {"level": 55}. cargo xtask ci: all 10 steps passed.
2026-10-23 09:42:07 +07:00
Jürg Lehni c28ad99678 Type: OpenType SVG colour fonts draw and export as vector art
Each glyph's SVG document is read by the SVG importer, which the engine registers with the text crate.
2026-10-23 09:07:54 +07:00
Angelo Bernardini ef1fa1d228 G12: Constrain Angle for the arrow keys, Shift drags and new objects (#394)
Squashed from #3363 onto main.
2026-10-23 08:54:57 +07:00
Louise FoxandClaude Opus 5.5 79cb38f678 Plug-ins: heavy filters, allowed at install and run in the background (G17)
A filter plug-in gets 50 M instructions and a minute for each run.
Filters that order a plotter's paths, pack shapes or run a simulation
need minutes, and the app shouldn't freeze while they compute.

A filter's manifest may now ask for "budget": "heavy" (Budget): each run
gets HEAVY_FUEL (1 G instructions) and HEAVY_WALL_TIME_MS (300 s). Only a
filter may ask. plugin.install refuses such a filter without allowHeavy:
true, and plugin.inspect returns the entry a module would have without
installing it. Install Plug-in… and File › Open ask the user and install
on OK; the plug-in store keeps the answer, and folder loads skip heavy
filters with a note, since they ask nobody.

The app runs a heavy filter on a thread of its own (plugin_runs), not in
Background Save's queue, with the status bar and a progress window
(Cancel, Continue in Background) that it shares with Background Export
(export_progress::window). FilterRun splits plugin.run: prepared on the
session, run on any thread, its output handed in, and the next
plugin.run with the same params applies that output instead of running
the plug-in again, so the edit stays one guarded, journaled command. A
result whose document or plug-in changed meanwhile is dropped, and the
status bar says to run it again. A cancelled plug-in run is now
EngineError::Cancelled.

What a filter keeps with the document (#3720) goes with the run: it is
given _context.documentData when the run is prepared, and the
documentData of its output is applied with its objects, in the same undo
step, when the run is handed in (FilterRun::finish).

Tests: plugins/tests/abi.rs (100 M loop steps out of a light budget,
within a heavy one; folders skip heavy filters), manifest.rs, engine
tests_plugins.rs (consent, inspect, the store, hand-in, a handed-in
run's document data, cancel) and
ui-egui tests_plugin_runs.rs (installing asks, no Preview, a background
run as one undo step, a stale result kept out, the progress window).
CRAFT_UI_HEAVY_FILTER_WASM=<folder> writes a test filter to try in the
app. Its strings and messages have rows 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 08:18:47 +07:00
Angelo Bernardini 22bdcae44d G4: hanging punctuation keeps its amounts under Glyph Scaling and burasagari
Squashed from #3362 onto main.
2026-10-23 07:35:24 +07:00
BFlat b94dc0db2c Merge pull request #3733 from storytold/land/3361
G4: paragraph Auto Leading drives the Leading field, leading nudge and placeholder fill
2026-10-23 07:25:29 +07:00
Angelo BernardiniandClaude Opus 5.5 2cf37aea2f G4: paragraph Auto Leading drives the leading panel, nudge and placeholder fill
Hyphenation... and Justification... already shipped upstream (#2310,
#2755). This ports the part of the archived fork PR #10 that upstream
lacked: a paragraph's Justification > Auto Leading now also sets

- the Leading the Character (and Properties) panel shows for Auto type,
  blank where the selected paragraphs differ;
- the leading Alt+Up/Down (type.step) steps from, per paragraph (a run
  spanning paragraphs is split so each takes its own);
- the lines Fill with Placeholder Text counts in area type.

ParaStyle::effective_leading mirrors the layout (exactly
CharStyle::effective_leading at the default 120%). Tests in doc, engine
and ui-egui; parity docs no longer list Hyphenation/Justification as
missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
2026-10-23 07:02:49 +07:00
Muhammad Saad 674a485145 Brushes: one output budget across tracks, tiles and copies (#3594)
Pattern tiling and scatter cap each factor on its own: 20,000 tiles a
run, MAX_SUB subdivisions a segment, 20,000 copies a track. Those caps
are per track, and a path has as many tracks as it has subpaths, so
they multiply rather than bound.

The audit called the end-to-end impact unverified, so it was measured.
A pattern brush on parallel lines, each long enough to want more tiles
than a run allows:

   1 subpath    20,000 nodes     320,000 anchors    36 ms
   4 subpaths   80,000 nodes   1,280,000 anchors   142 ms
  16 subpaths  320,000 nodes   5,120,000 anchors   571 ms

Exactly 20,000 nodes a subpath, linear with no ceiling. Scatter is the
same shape.

MAX_ANCHORS bounds the total for one brushed stroke, threaded through
both. It is 2,000,000, the figure the stacked-effects budget already
uses (effects::distort::MAX_COPY_ANCHORS, #3578), and bounded the same
way: per stroke rather than per document. One track at its own cap is
well inside it, so the budget bites only where the caps were
multiplying; sixteen subpaths now stop at the budget in 113 ms.

A piece is taken whole or not at all, so art is never half-drawn. The
walk that counts an art tile's anchors is iterative, and a tree it
cannot finish within MAX_WALK nodes is charged the whole budget rather
than undercounted, so nesting buys no room.

The per-factor caps are untouched: the issue asks for a total, and
changing both at once would hide which one did the bounding.
2026-10-23 06:56:56 +07:00
Angelo Bernardini 5e89b11685 G11 #834: bezier drag previews follow the pointer to the release
Squashed from #3360 onto main.
2026-10-23 06:30:19 +07:00
Taha-Mahmoodi 970e62f27d Fix: a path moved by its stroke or edge lands exactly on a ruler guide
Cause: with Snap to Point, the point of a moved selection that snaps to a
ruler guide is the grabbed point (#1534). Away from an anchor or center
that was the raw pressed spot (select.rs: `grab` fell back to `p`;
direct.rs: a straight segment's `grab: p`, MoveObject's `start`). A press
on a stroke or within the pick tolerance of an edge lies a few pixels
beside the path, so the pressed spot landed on the guide, "align" showed,
and the path stopped that far beside it: 0.096 pt at 3,891% and 0.0099 pt
at 28,757% in the #3701 screenshots, visible only when zoomed in.

Change: `guides::hold_on_path` finds the nearest point of the path (on a
straight segment exactly on its line). `select::hold_point` uses it for a
press on the stroke or outline, or within 6 px of it. The Selection tool
(`State::Moving::hold`), Direct and Group Selection moves
(`State::MoveObject::hold`) and Direct Selection's straight-segment drag
(`grab`) snap that point. The move delta still runs from the press, so
nothing jumps. A press in the middle of a fill still snaps the pressed
spot, as Snap to Point is documented to.

Verification: tests_prefs::a_move_held_by_a_paths_stroke_lands_the_path_on_a_ruler_guide
and tests_prefs::a_straight_segment_dragged_by_direct_selection_lands_on_a_ruler_guide
fail without the change (edge 0.0257 pt and anchors -0.0052 pt off the
guide) and pass with it (|gap| < 1e-9 pt). A headless egui/wgpu render at
3,891% and 28,757% puts the guide, the selection outline and the centre of
the 1 pt stroke band in the same pixel column. cargo xtask ci: all 10
steps passed; 9429 tests passed, 0 failed.
2026-10-23 06:13:02 +07:00
Angelo Bernardini 3f16fabd89 G11 #885: Transform effect options (Transform Objects, Transform Patterns, Scale Strokes & Effects)
Squashed from #3358 onto main.
2026-10-23 06:07:37 +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
Jürg Lehni 9de4b40241 Type: COLR colour fonts draw and export as vector art
Glyphs whose paints documents have no form for (sweep gradients, compositing modes) are drawn as images of the glyph.
2026-10-23 05:14:47 +07:00
Angelo BernardiniandClaude Opus 5.5 e7233ef0c5 G11 #937: Shape Builder cuts art of thousands of segments with lines again
The planar map found each edge's source input by scanning every input
segment, and the face around each hole or merge point by scanning every
face, so it was quadratic in the segments; the Shape Builder therefore
gave up past 4096 segments (traced or type outlines) and offered whole
shapes. Index outline segments and faces by bounds on both axes
(RectIndex), walk an edge's segments lazily when finding its leaving
angle, and raise SHAPE_BUILDER_MAX_SEGMENTS to 65,536. Divide with open
paths shares the cap; its cap test now follows the constant.

Tests: a 20,000-segment blob cut by two lines, a 300-line hatch, the
index against a brute-force scan, and the existing cap test at the new
cap. Progress docs updated (G11, parity evidence, roadmap).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
2026-10-23 04:48:58 +07:00
FractalEncrypt 974bca0c9d G1.docs: Reconcile newer upstream assessment while retaining scaleArt evidence 2026-10-23 04:48:37 +07:00
FractalEncrypt 455c0df29d G1.docs: Reconcile newer upstream assessment while retaining scaleArt evidence 2026-10-23 04:48:36 +07:00
FractalEncrypt f63ba14378 G1.docs: Reconcile newer upstream assessment while retaining scaleArt evidence 2026-10-23 04:48:34 +07:00
FractalEncrypt e034f248d9 G1.docs: Reconcile newer upstream assessment while retaining scaleArt evidence 2026-10-23 04:48:33 +07:00
FractalEncrypt 3d7f0bb81e G1.docs: Preserve latest upstream assessment in scaleArt follow-up 2026-10-23 04:48:32 +07:00
FractalEncrypt c26d809ea2 G1.docs: Correct shared-metadata descriptions in scaleArt follow-up evidence 2026-10-23 04:48:30 +07:00
FractalEncrypt 205f65b7a3 G1.docs: Reconcile scaleArt follow-up evidence with current upstream history 2026-10-23 04:48:30 +07:00
FractalEncrypt a6c0911d72 G1: Honor artwork scaling in Artboard Options size changes 2026-10-23 04:48:28 +07:00
Angelo Bernardini 16d39d7710 G11 #722: keep imported PDF text boxes in place
Squashed from #3356 onto main.
2026-10-23 04:47:59 +07:00
Taha-Mahmoodi edca5e9eb0 Docs: the Effect Gallery is shipped; thumbnails and an in-dialog preview area remain (G2 1–2 h, raster effects 85%) 2026-10-23 03:48:07 +07:00
bflatastic 8bf7ffe39d Snapping: tangent and perpendicular snapping, View > Snapping (#2530)
Squashed from #2537 onto main.
2026-10-23 03:22:54 +07:00
Brian TorresandClaude Opus 5.5 54c8a7f2bd Effects: stacked geometry effects keep to an anchor and line budget (#3578)
A chain of geometry effects makes at most 2 million anchors of a path (of a
type, symbol or image art's paths together) and about 8 million lines for the
renderer to draw at 1 px/pt, or what the path costs when that is more.
Roughen spaces its points further apart, Zig Zag makes fewer ridges a
segment, Free Distort, Twist and the warps split segments into longer pieces
(map_nonlinear_within) and Transform makes fewer copies where they would pass
it; a result drawn with too many lines is built again with fewer points, and
an effect that still doesn't fit is left out.

Three Roughens of a rectangle in a 25 KB file needed gigabytes of geometry,
and 2 million anchors of them flattened into 141 million lines (7 GB, past
vello's 2^27 lines a path); the file now opens in 0.4 s at about 450 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:44:52 +07:00
Taha-Mahmoodi 6beb68f934 Gradients: the Gradient panel's Method menu and paint.editGradient's method choose Classic or Perceptual
Illustrator 30.0's Method choice in the Gradient panel
(https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-perceptual-interpolation-in-gradients.html).

- paint.editGradient takes `method: classic|perceptual` (one undo step);
  paint.setFill/setStroke take it in `gradient`, and gradient params (swatches,
  copied paints) carry it when Perceptual. Anything else is refused without
  editing. One edit can set `method` and `dither` together.
- The Gradient panel shows a Method menu (Classic, Perceptual) for linear and
  radial gradients, dimmed without a gradient; the Dither box stays under the
  stops.
- i18n: "Perceptual" where a complete catalog lacked it, and the `method`
  error in all 24 catalogs.
- Docs: G13 (estimate 14–24 h → 13–23 h, after Dither's 15–25 h → 14–24 h),
  target-app-parity (row and evidence), ui-parity, mcp.md, plugins.md.
2026-10-23 01:53:30 +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
Mirko Kerezovic d548b4ff74 Fix PDF title display provenance 2026-10-23 01:01:25 +07:00
Mirko Kerezovic 5e9df83dd4 Fix #3646: display PDF document title 2026-10-23 01:01:24 +07:00
bflatastic 26b9cf84ad Type: colour emoji fonts with bitmap glyphs draw and export
Squashed from #3561 onto main.
2026-10-23 00:44:31 +07:00
Taha-Mahmoodi cc1a660a6d Gradients: Dither in the Gradient panel draws a gradient fill with fine noise that hides banding
Illustrator 30.0's gradient Dither, from the public documentation
(https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-dither-in-gradients.html):
a Dither checkbox in the Gradient panel; dither converts the gradient into
pixels with subtle noise, works with gradient fills only, and follows the
Document Raster Effects resolution.

- Gradient gains `dither` (serde default false, written only when on, so
  older files open unchanged and new files keep it).
- The renderer draws a dithered linear or radial fill as an image paint: the
  gradient sampled a cell per device pixel or finer (quarter-octave steps,
  cached per thread), each channel given seeded triangular noise of up to a
  level before rounding. Same pixels every run; strokes and undithered fills
  draw exactly as before. PNG and other raster exports go through it.
- SVG and PDF write a dithered fill as an image at the document's raster
  effects resolution, clipped to the shape (the freeform-gradient path);
  characters' fills stay vector gradients in SVG, with a warning.
- paint.editGradient and paint.setFill take `dither`; strokes refuse it.
  The Gradient panel's Dither box is dimmed behind the Stroke proxy.
- i18n rows in all 24 catalogs; docs: G13, target-app-parity, ui-parity,
  mcp.md, plugins.md.
2026-10-22 22:38:42 +07:00
Taha-Mahmoodi 9097c4a564 Docs: G2 lists the 57 filters once and its table estimate matches; ROADMAP records Conté Crayon in its revision history
Review follow-up (CodeRabbit): the two older G2 bullets counting 46 and 49
filters were superseded by Conté Crayon (57/57) and are folded into one
current bullet; the G2 row in the ranked table and the entry's estimate now
both say 4–6 h, as ROADMAP.md and roadmap.md do. ROADMAP.md's revision
history gains its 2026-10-22 row.
2026-10-22 21:34:37 +07:00