Commit Graph
5797 Commits
Author SHA1 Message Date
dotCooCoo a44c9a2d0b Tests: the --help tests wait for the app to exit on a loaded machine
`apps/vectorcraft/tests/it/help.rs` gave the desktop binary 5 seconds
to exit and otherwise reported that the app had started. While
`cargo xtask ci` runs the workspace's tests in parallel, starting the
binary can take longer than that, and both help tests failed although
the binary printed its usage.

The test now reads standard output and standard error to their end on
threads and waits up to 120 seconds for them. It also sets
`VECTORCRAFT_CONTROL_PORT` to a value the app rejects, so a binary that
does not stop at `--help` exits with that error instead of opening a
window, and the usage checks fail.
2026-10-26 20:34:42 +07:00
BFlat 7c4e3ce103 Merge pull request #4261 from dotCooCoo/fix/image-cache-reads-installed-memory
Render: the decoded-image cache sizes from installed memory on Windows, macOS and FreeBSD
2026-10-26 20:20:39 +07:00
dotCooCoo 16205440e2 Render: the decoded-image cache sizes from installed memory on Windows, macOS and FreeBSD
The renderers share decoded images within a budget of an eighth of the
installed memory, at most 1 GiB. The installed memory was read only on
Linux, so on Windows, macOS and FreeBSD the budget was always 1 GiB,
also on machines with less than 8 GiB.

The installed memory is now read with GlobalMemoryStatusEx on Windows
(through winsafe, which vectorcraft-cli already uses), sysctl
hw.memsize on macOS and sysctl hw.physmem on FreeBSD. The budget rule
is unchanged: an eighth of the installed memory, at most 1 GiB, 1 GiB
where the memory can't be read, and 256 MiB in the web build.
2026-10-26 20:18:33 +07:00
BFlat 949ca60bff Merge pull request #4260 from nrq6993-a11y/stream/vc-feat3-g4-mixed-small-caps-position
G4: mixed Small Caps and super/subscript in Character panel
2026-10-26 20:16:14 +07:00
nrq6993-a11y b467f37d1d G4: mixed Small Caps and super/subscript in Character panel
Squashed from #4260 onto main.
2026-10-26 20:12:52 +07:00
BFlat 6e191ccf14 Merge pull request #4258 from nrq6993-a11y/stream/vc-featD-g8-eps-dedupe-negative-operand-tests
Clippy fixes after upstream merge
2026-10-26 19:51:33 +07:00
nrq6993-a11y fc9ced75d9 Clippy fixes after upstream merge
Squashed from #4258 onto main.
2026-10-26 19:51:06 +07:00
BFlat 66277c3e68 Merge pull request #4093 from dotCooCoo/feat/list-convert-to-text
Type: Convert to Text makes a list's bullets and numbers characters of its text
2026-10-26 19:49:50 +07:00
dotCooCoo 7a815b0acc Type: text.convertListToText moves the Type tool's caret past the markers
The Type tool's anchor and caret were moved past the inserted markers
only by the menu item in the desktop app. When text.convertListToText
came over MCP or the control channel while the Type tool was editing,
they kept their old byte offsets, and the inserted characters (a bullet
is 3 bytes) could leave an offset inside a character.

The command now ends the Type tool's typing, then moves its anchor and
caret past the characters inserted at or before them. In threaded type,
when the caret's character moved to another frame, the tool edits that
frame, which becomes the selection when it was not selected. The menu
item runs the command without adjusting the selection itself.

Docs: docs/mcp.md.
2026-10-26 19:38:12 +07:00
dotCooCoo 2f445e117f Type: Convert to Text makes a list's bullets and numbers characters of its text
The reference app's Type › Bullets and Numbering › Convert to Text turns
the bullets and numbers of the selected list paragraphs into editable
text. VectorCraft drew list markers but could not convert them.

Type › Bullets and Numbering › Convert to Text (type.convertListToText,
text.convertListToText) puts each list paragraph's marker at the start
of the paragraph as characters: the bullet, or the number and its
period as the paragraph showed it, then a tab, in the marker's font,
size and color. The paragraph leaves its list. Where the marker's tab
went to the left indent, the paragraph gets a plain left tab stop
there, so the text after the tab starts where it did. Where the
marker's tab went to a stop with a leader, or to a right, center or
decimal stop, a plain stop where the tab ended keeps the converted tab
there without the leader; right, center and decimal stops between the
marker and that place are removed. Past the left indent this applies
only to a paragraph with no tab of its own. In threaded type the
numbers are those the story counted, and a paragraph that goes on into
the next frame is converted once, where it starts. A right-to-left
paragraph with no direction set gets the Right to Left direction when a
letter or roman numeral in front of it would make it run left to right.
The change is one undo step. The Type tool's caret stays next to the
character it was next to, and in threaded type it goes with that
character to the frame it moved to, which becomes the selection when it
was not selected.

With the Every-line Composer a paragraph that holds a tab is broken
line by line, so a converted paragraph can break its lines at other
places than the list paragraph did.

Docs: docs/mcp.md, docs/target-app-parity.md.
i18n: Convert to Text in the 24 catalogs.
2026-10-26 19:38:12 +07:00
BFlat 7c13424afc Merge pull request #4255 from dotCooCoo/fix/undecodable-image-placeholder
Render: an image that can't be decoded shows as a crossed box and isn't decoded again
2026-10-26 19:12:46 +07:00
dotCooCoo bdba8c1cea Render: an image that can't be decoded shows as a crossed box and isn't decoded again
An image whose pixels can't be decoded, such as a PNG cut short by an
interrupted download, drew nothing on the canvas and the status bar
said nothing. Every render decoded it again, so each frame that drew it
paid for a decode that fails.

The renderer now notes such an image by its content and does not decode
it again. Each render still counts it in decode_failures, so an export
reports it as before. An image refused only because the images being
drawn leave it no room within twice the decoding allowance is not
noted, and the next render tries it again.

On the canvas the image draws as a light gray box crossed from corner
to corner, and the status bar says once for each image that it couldn't
be decoded. Exports still draw nothing there.

Docs: docs/target-app-parity.md.
2026-10-26 19:00:28 +07:00
BFlat 9fc09feb80 Merge pull request #4254 from nrq6993-a11y/stream/vc-issue-3694-anchor-space
G11: Anchor Point tool Space moves anchor mid-drag
2026-10-26 18:53:43 +07:00
nrq6993-a11y 9f74540d58 G11: Anchor Point tool Space moves anchor mid-drag
Squashed from #4254 onto main.
2026-10-26 18:49:33 +07:00
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