Commit Graph
5783 Commits
Author SHA1 Message Date
BFlat 507a0e3bc1 Merge pull request #4253 from nrq6993-a11y/stream/vc-featD-g8-art-style-dedupe-tests
Clippy: bool and range checks after egui 0.37 merge
2026-10-26 18:49:00 +07:00
BFlat 3de018b3ae Merge pull request #4252 from nrq6993-a11y/stream/vc-feat-g1-blob-brush-scrub-length
G1: Blob Brush drag shows scrub path length when Measurement Labels on
2026-10-26 18:48:45 +07:00
BFlat 1261d7d7fb Merge pull request #4251 from dotCooCoo/fix/pdf-drawn-nbsp-thin-space
Part of #2384: imported PDF text keeps its place beside a drawn no-break or thin space
2026-10-26 18:48:34 +07:00
nrq6993-a11y a0b038e9e2 Fix clippy after upstream merge (art style test, Fenwick step, pencil bool)
Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-26 18:46:53 +07:00
nrq6993-a11y 37995765a1 G8: AI art style and gradient import regression tests for hostile values
## What and why

Hostile `.ai` art styles and gradients (out-of-range caps, joins, miter limits, dash arrays, stroke widths, width profiles, blend opacity, paint flags, operand counts, too many gradient stops) must be clamped or ignored on import. The tests only: the limits and checks they cover are already on main, so the tests pin that behaviour so a later change can't silently drop it.

## How it was tested

The new tests pass, along with the full `cargo xtask ci` on the branch.

Combines the tests from: stream/vc-featD-g8-art-style-blend-opacity, stream/vc-featD-g8-art-style-cap-join, stream/vc-featD-g8-art-style-miter-cap, stream/vc-featD-g8-art-style-operands-cap, stream/vc-featD-g8-art-style-paint-flags, stream/vc-featD-g8-art-style-stroke-width, stream/vc-featD-g8-gradient-stops-cap, stream/vc-featD-g8-art-style-dash-cap, stream/vc-featD-g8-art-style-width-profile-cap
2026-10-26 18:46:49 +07:00
bflatastic f024069002 Effects and pathops: keep the isolate_lowest_one allows #4252 dropped (clippy 1.99 still flags the manual form) 2026-10-26 18:46:46 +07:00
nrq6993-a11y c38185f79c ci: Rust 1.95 clippy and flaky Arabic PDF test after upstream merge
Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-26 18:40:47 +07:00
nrq6993-a11y 789b69ce8e G1: Blob Brush drag shows scrub path length when Measurement Labels on
Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-26 18:40:46 +07:00
dotCooCoo bba3537ea3 Part of #2384: imported PDF text keeps its place beside a drawn no-break or thin space
A space the file draws between two words gets tracking of its own so the
word after it stays where the file put it, but only U+0020 counted as a
space. A drawn no-break space (U+00A0, U+202F) or fixed-width space
(U+2000 to U+200A, thin and hair spaces included) took the old path: the
gaps on both sides of it also read as spaces, so the line came in with
an extra space before and after it and the words after it out of place.
These characters now count as spaces in the line's gap and drawn-space
rules, and a space of more than one byte is split into its own run whole.

The wrapped-line space tolerance in area type, a fiftieth of an em, is
named SPACE_TRACKING_TOLERANCE.
2026-10-26 18:39:20 +07:00
BFlat 1a84e18097 Merge pull request #4250 from DCaioP/fix/4128-text-create-installed-font-style
Fix #4128: text.create and text.setStyle store a font style the family has
2026-10-26 18:16:53 +07:00
Caio Pontes 311b343219 Fix #4128: text.create and text.setStyle store a font style the family has
Given a family and no font style, text.create and text.createInPath
kept the default Regular, and text.setStyle kept the text's previous
style, even when the family has no such style. A family whose styles
are numbered (Hiragino W3, W6) has no Regular, so the file named a
font that isn't installed and opened with Missing Fonts.

They now keep the style when the family has it, else take the
family's closest one, as text.setRangeStyle already did. Its rule
moves to `textedit::fit_font_style`, which the three commands share;
Graph labels styled through text.setStyle follow it too. A style the
caller names is kept as given.

The test builds a family with W3 and W6 and no Regular in code
(`test_fonts::numbered_font`), so no font file is added.
2026-10-26 18:12:51 +07:00
BFlat 8fa61c4aa2 Merge pull request #4130 from xbirks/fix/windows-gdi-only-fonts
Windows fonts: list the fonts only GDI has loaded, such as Creative Cloud's Adobe Fonts (#1258)
2026-10-26 17:55:44 +07:00
BFlat 9badc5619a Merge pull request #4247 from ashokram-si/feat/named-views
View: all 25 saved views in the View menu; Edit Views… lists them
2026-10-26 17:53:28 +07:00
Andrés Ortega 88060b199f Windows fonts: list the fonts only GDI has loaded, such as Creative Cloud's Adobe Fonts (#1258)
Squashed from #4130 onto main.
2026-10-26 17:51:42 +07:00
ashokram-si 87470ef0b7 View: all 25 saved views in the View menu; Edit Views… lists them
A document keeps up to 25 saved views, but the View menu listed only the
first 10, so views 11 to 25 could be saved and never chosen. All 25 are
listed now, by name, and choosing one changes the current window to its
zoom, centre and rotation (within the canvas's zoom range).

Edit Views… used to open the generic parameter dialog on the first view,
where the name of the view to change had to be typed. It now opens a list
of the document's views, like Edit Selection…: pick one to rename it or
delete it; OK applies every change as one undo step and Cancel leaves the
views as they were. view.saved.edit takes {edits: [{name, newName?,
delete?}…]} for that, the one-view form still works, and the command is
off while the document has no saved view.

A file's views are untrusted: on open at most 25 are kept, names are
trimmed, capped at 255 characters and unique, and views without a finite
centre and a positive zoom are dropped. New View… starts from the first
free "View N" and caps names the same way.

Behaviour from the public help page "Use multiple windows and views":
https://helpx.adobe.com/illustrator/using/viewing-artwork.html

docs/gaps.md (G7) and docs/target-app-parity.md take New View/Edit Views
off the missing list; docs/mcp.md documents the view.saved commands; the
catalogs get the Saved View 11-25 rows in the pattern of 1-10.
2026-10-26 17:40:42 +07:00
BFlat 751c4ee144 Merge pull request #4243 from dotCooCoo/fix/undo-scope-cancel-forgets-cut-journal-entries
Undo: a canceled undo scope drops the journal entries it cut from open undo groups
2026-10-26 17:02:11 +07:00
dotCooCoo 389dce727d Undo: a canceled undo scope drops the journal entries it cut from open undo groups
When an undo scope that joined an open undo group was canceled, the
journal was cut back to where the scope began, but the group still
listed the cut entries' numbers. The next command took one of those
numbers, so a later cancel of the group also removed that command's
entry, even when it ran in another document, and a replay of the
journal no longer matched the documents. end_undo_scope now removes
the cut numbers from every open undo group's journal entries and batch
steps.
2026-10-26 16:58:49 +07:00
BFlat b44d2500e3 Merge pull request #4240 from dotCooCoo/fix/brush-art-symbol-renames
Render: brush art is made again when a symbol or pattern it shows is renamed
2026-10-26 16:40:00 +07:00
dotCooCoo 1edfb8f450 Render: brush art is made again when a symbol or pattern it shows is renamed
The brush cache dropped brush art that can show a symbol or a pattern
only when the art of the document's symbols or patterns changed. Renaming
one, for example swapping the names of two symbols, left their art as it
was, so a brush whose art holds an instance of one of those names kept
drawing the old symbol. The cache now compares the names along with the
art.
2026-10-26 16:38:04 +07:00
BFlat 1cdc98735c Merge pull request #4238 from nrq6993-a11y/stream/vc-featD-g8-eps-dedupe-typecheck-tests
G8: EPS import regression tests for operator type checks
2026-10-26 16:08:42 +07:00
BFlat a2014395bb Merge pull request #4144 from Taha-Mahmoodi/feat/port-double-click-breaks-thread
Type: a double-click on a threaded port breaks the thread there
2026-10-26 16:06:52 +07:00
nrq6993-a11y 97652362fa G8: EPS import regression tests for operator type checks
## What and why

Hostile EPS programs that give an operator the wrong operand type (a non-integer count, a non-procedure body, a non-boolean condition) must stop the import with a `typecheck` error instead of misbehaving. One test per operator: copy, for, idiv, if, ifelse, index, loop, mod, repeat, roll and srand. The tests only: the limits and checks they cover are already on main, so the tests pin that behaviour so a later change can't silently drop it.

## How it was tested

The new tests pass, along with the full `cargo xtask ci` on the branch.

Combines the tests from: stream/vc-featD-g8-eps-copy-typecheck-count, stream/vc-featD-g8-eps-for-typecheck-procedure, stream/vc-featD-g8-eps-idiv-typecheck-operand, stream/vc-featD-g8-eps-if-typecheck-condition, stream/vc-featD-g8-eps-if-typecheck-procedure, stream/vc-featD-g8-eps-ifelse-typecheck-condition, stream/vc-featD-g8-eps-ifelse-typecheck-procedure, stream/vc-featD-g8-eps-index-typecheck-count, stream/vc-featD-g8-eps-loop-typecheck-procedure, stream/vc-featD-g8-eps-mod-typecheck-operand, stream/vc-featD-g8-eps-repeat-typecheck-count, stream/vc-featD-g8-eps-repeat-typecheck-procedure, stream/vc-featD-g8-eps-roll-typecheck-j, stream/vc-featD-g8-eps-roll-typecheck-n, stream/vc-featD-g8-eps-srand-typecheck-operand
2026-10-26 16:06:20 +07:00
Taha-Mahmoodi de151f849a Type: the thread-break comments describe the behaviour in our own words
Review follow-up (CodeRabbit, clean-room): two doc comments quoted the
cited help page. Comments only.
2026-10-26 16:00:39 +07:00
Taha-Mahmoodi 54d9fbc162 Type: a double-click on a threaded port breaks the thread there 2026-10-26 16:00:39 +07:00
BFlat b25fdead4f Merge pull request #4236 from dotCooCoo/fix/type-on-a-path-tool-options
Toolbar: double-clicking the Type on a Path tool opens Type on a Path Options
2026-10-26 15:47:36 +07:00
dotCooCoo 45440b5444 Toolbar: double-clicking the Type on a Path tool opens Type on a Path Options
A double-click on the Type on a Path or Vertical Type on a Path tool
reported that the tool has no options. It now opens Type on a Path
Options for the selected type on a path, the same dialog as Type ›
Type on a Path › Type on a Path Options…. With nothing selected, or
with no type on a path selected, nothing opens and `tool.options`
returns the menu command's error, as the Rotate tool's double-click
does without a selection.

Docs: docs/control-protocol.md, the `tool.options` params doc.
2026-10-26 15:44:50 +07:00
BFlat af3e049d46 Merge pull request #4141 from Taha-Mahmoodi/feat/pathfinder-options
Fix #3426: the Pathfinder panel's Pathfinder Options… sets Precision, Remove Redundant Points and Divide and Outline Will Remove Unpainted Artwork
2026-10-26 15:37:14 +07:00
BFlat ea5c020da7 Merge pull request #4235 from Atalantis/macos-dialog-localizations
macOS: declare the UI languages so the open and save panels are translated
2026-10-26 15:36:58 +07:00
BFlat 660f485243 Merge pull request #4020 from Taha-Mahmoodi/feat/brush-libraries
Brushes: Window › Brush Libraries opens brush libraries whose brushes click or drag into the document's Brushes
2026-10-26 15:29:24 +07:00
Taha-Mahmoodi cfd085f34a Fix #3426: the Pathfinder panel's Pathfinder Options… sets Precision, Remove Redundant Points and Divide and Outline Will Remove Unpainted Artwork
Squashed from #4141 onto main.
2026-10-26 15:28:51 +07:00
Florent Siegenthaler a8345dcfb1 Declare the UI languages to AppKit so the open and save panels are translated 2026-10-26 15:28:39 +07:00
Taha-Mahmoodi 0a241a7fdd Brushes: Window › Brush Libraries opens brush libraries whose brushes click or drag into the document's Brushes
Squashed from #4020 onto main.
2026-10-26 15:17:30 +07:00
BFlat a1ef364191 Merge pull request #4234 from ham9zah/fix/ai-blend-check-followups
Import: live blend check follow-ups
2026-10-26 15:15:24 +07:00
ham9zah 9a05383f34 Import: live blend check follow-ups
Review follow-ups to the live blend import (#4127):

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

Tests: a laid object matches one drawn step, 1e300 coordinates in the
check and in the import fuzz property, and a record line longer than
the record.
2026-10-26 15:11:45 +07:00
BFlat 853f4363ca Merge pull request #4127 from ham9zah/fix/ai-live-blends
Import: Illustrator blends come in as live blends
2026-10-26 14:23:53 +07:00
BFlat 4ddab29960 Merge pull request #4142 from Taha-Mahmoodi/feat/delete-empty-artboards
Artboards: the Artboards panel's Delete Empty Artboards deletes every artboard with no artwork in one undo step
2026-10-26 14:23:37 +07:00
ham9zah 4214d4c9d3 Import: live blends are checked in bounded time and keep no stray art
Review follow-ups for the live blend import:

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

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

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

Hand-written tests: two keys and a straight spine as one live blend with
three steps and interpolated colours; a curved spine (l, c, L) in both
directions keeping its points and the keys' order; a garbled or short
record, a key without an id and ids the record doesn't list keeping the
drawn steps with a note; moving a key moving the steps; record parsing
on hostile input. The import fuzz target gains the record and the new
mesh keys.
2026-10-26 14:10:37 +07:00
Taha-Mahmoodi a34055beec Artboards: the Artboards panel's Delete Empty Artboards deletes every artboard with no artwork in one undo step
Squashed from #4142 onto main.
2026-10-26 14:10:36 +07:00
BFlat 0b5affef5e Merge pull request #4166 from Taha-Mahmoodi/fix/2691-repeat-grid-handles
Repeat: a selected Grid Repeat has on-canvas handles for its rows, columns and spacing (part of #2691)
2026-10-26 13:49:33 +07:00
BFlat b36d9b7160 Merge pull request #4230 from Silvertree2010/fix/h2780-svg-import-css-declaration-parse
Respect quotes and parentheses when splitting SVG CSS declarations
2026-10-26 13:46:38 +07:00
Taha-Mahmoodi 5b91057dca Repeat: a selected Grid Repeat has on-canvas handles for its rows, columns and spacing
With the Selection tool, a single selected grid repeat shows a handle below the grid (rows), one right of it (columns), one past its bottom-right corner (both) and sliders between its first two columns and rows (horizontal and vertical spacing), as the public Set grid repeat options page describes. Each drag previews object.repeat.options and commits as one Repeat Options undo step; Escape cancels, and a drag that ends where it began leaves no step. Bounding-box handles keep priority where they overlap, and the controls hide with the edges or the bounding box. Before, a drag there scaled the source or started a marquee.

Part of #2691.
2026-10-26 13:44:06 +07:00
NetterSilverandClaude Haiku 5.5 29a98b750a Respect quotes and parentheses when splitting SVG CSS declarations
Found while testing crates/svg/src/import/css.rs:21 (parse_decls)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 13:43:20 +07:00
BFlat bb3ee0a260 Merge pull request #4210 from KaboomFox/pdf-tests-dated-alike
PDF tests: exports compared byte for byte are dated alike
2026-10-26 13:40:00 +07:00
BFlat ddc72e4886 Merge pull request #4227 from ncic/pencil-mouse-samples
Canvas: a mouse's moves between frames reach the tool too
2026-10-26 13:38:20 +07:00
Louise FoxandClaude Opus 5.5 1466317301 PDF tests: exports compared byte for byte are dated alike
dithered_perceptual_gradients_export_as_images_with_noise_on_the_fill
compares two exports byte for byte: a stroke's file with dither and
without. The export dates a file by the clock, in whole seconds
(metadata::output_time, unless SOURCE_DATE_EPOCH is set), and the
tests' `uncompressed` helper gave no date of its own. So the test
failed whenever a second ticked between the two exports: the files
then differed in /CreationDate, /ModDate, the XMP dates and the IDs
made from them, and in nothing else. CI hit it on #3807.

With the wall clock moved a second on each read (an LD_PRELOAD shim
on clock_gettime), the test failed 5 runs of 5, and it was the only
one of vectorcraft-pdf's 380 tests that did. With the helper's date
fixed it passes, as do the other 379.

The helper now dates its exports `created: Some(0)`, as the crate's
other tests that compare whole files do (tests_editing, tests_pdf13,
tests_webview).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-26 13:37:40 +07:00
ncicandClaude Opus 5.5 21a9e854e1 Tools: only the freehand tools get every move of a frame (#4227 review)
Every pointer move of a frame went to whatever tool was active, so with
a fast mouse (or a pen) a tool whose drag preview is costly (the
Selection tool moving a large selection, Free Distort, the Width tool)
redid it for each, up to 64 times a frame.

Tool::wants_samples (default false) says a tool builds a stroke from
the drag's samples: the Pencil, Paintbrush, Blob Brush, Eraser, Knife,
Smooth and Path Eraser answer true (the Paintbrush through its wrapper),
the Join tool doesn't. The canvas still reads the frame's moves every
frame, so the macOS feed's pressures stay paired, but replays them only
to such a tool; every other tool gets one drag a frame at the latest
position. A frame whose moves end where the last frame's did still
reaches the tool.

Tests: only_the_freehand_tools_get_every_move_of_a_frame (Pencil,
Paintbrush and Blob Brush get the 19 moves before the last, Selection,
Direct Selection and Width none),
a_mouse_stroke_keeps_a_frame_that_ends_where_it_started,
the_freehand_tools_want_every_sample, and
a_mouse_stroke_keeps_every_move_of_a_frame now checks every position in
order, not only the count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 13:31:52 +07:00
ncicandClaude Opus 5.5 954d3f3116 Canvas: a mouse's moves between frames reach the tool too (#3328)
The canvas hands a dragging tool every position the pointer passed
within a frame for pens and fingers (touch events) and for a macOS pen
(the stylus feed's moves, #852), but a mouse got the frame's last
position only: one sample per frame. A fast mouse stroke kept a
position every 10 to 20 px, too few for any Fidelity to follow the
hand, and fewer still while the app is busy and frames come slower.

frame_moves now keeps a mouse's moves as well, without a pressure (the
tool takes the host's latest, a mouse's being full; a pen's on X11
among them), with the same bound of 64 moves for a stalled frame, as
touch and pen moves already reach every tool.

Tests: a_mouse_stroke_keeps_every_move_of_a_frame (20 moves in one
frame reach the Pencil; it got 2 points before this change), and
frame_moves_keeps_touch_feed_and_mouse_samples_with_their_pressure
replaces the test that a mouse's moves were left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 13:31:52 +07:00
BFlat e9eec8fdeb Merge pull request #4229 from nrq6993-a11y/stream/vc-feat3-g4-loaded-text-cursor
G4: draw loaded-text cursor for threaded overflow
2026-10-26 13:09:43 +07:00
nrq6993-a11y c640f40ce1 G4: draw loaded-text cursor for threaded overflow
Squashed from #4229 onto main.
2026-10-26 13:09:22 +07:00