Commit Graph
4947 Commits
Author SHA1 Message Date
BFlat e195fc94e2 Merge pull request #3676 from odgrim/fix/3562-svg-nested-image-depth
SVG import: images in images share the depth cap, SVGZ images one unpacking cap (#3562, #3563)
2026-10-23 01:42:22 +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
BFlat 4ee7173f11 Merge pull request #3683 from Silvertree2010/fix/h1324-color-to-hsb-returns-hue-360-for
Wrap to_hsb hue of 360 back to 0 for near-red colours
2026-10-23 01:23:57 +07:00
BFlat 3d9656024e Merge pull request #3682 from Taha-Mahmoodi/fix/dither-stroke-null-swatch
Gradients: paint.setStroke refuses a dithered gradient when "swatch" is null
2026-10-23 01:23:40 +07:00
BFlat 5b0e60f865 Merge pull request #3677 from addibble/perf/svg-import-single-pass
SVG import: read the text once for the prepass's markers, with the data URLs left out
2026-10-23 01:23:21 +07:00
BFlat 03bf93c203 Merge pull request #3681 from dotCooCoo/fix/package-skips-page-parsing-without-fonts
Package: reads placed pages and document previews only for the options that use them
2026-10-23 01:23:02 +07:00
BFlat 165fea7f9b Merge pull request #3667 from MiQ71/fix/3646-display-doc-title
Fix #3646: display PDF document title
2026-10-23 01:22:45 +07:00
NetterSilverandClaude Haiku 5.5 4a29fd4112 Wrap to_hsb hue of 360 back to 0 for near-red colours
Found while testing crates/color/src/lib.rs:142

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 01:06:55 +07:00
Taha-Mahmoodi e20c330bd3 Gradients: paint.setStroke refuses a dithered gradient when "swatch" is null
Review follow-up (CodeRabbit): the refusal skipped any request carrying a
`swatch` key, so `"swatch": null` with a dithered gradient got through.
Only a named swatch (a gradient swatch keeps its own dither) is exempt now.
The dither test sends `"swatch": null`; it fails without the change.
2026-10-23 01:01:28 +07:00
Drew DibbleandClaude Opus 5.5 a7ee168efe perf --deep: the embedded-picture import row reports a failed import instead of timing it
One import is checked first and its document kept through the memory reading after the row, so
a failure stops the run with its error rather than being timed as an import (CodeRabbit). The
row's comment now gives the picture's real size, about 2 MB as a PNG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 01:01:27 +07:00
Drew DibbleandClaude Fable 5.1 5b919c0529 SVG import: read the text once for the prepass's markers, with the data URLs left out
The prepass decided what to look for in the tree by a dozen substring searches over the whole
SVG text (`:not(`, the event attributes, `script`, `transform:` and nine more), and a separate
pass counted the elements' depth before roxmltree. With an embedded picture the text is mostly
its data URL: a 1.35 MB card SVG (a 968 KB PNG) paid about 7 ms of searches and 1 ms of depth
scan per import, on a 64 ms render, and a 54-page PDF export 54 times that.

`depth::prescan` is the one pass: it counts the depth as `xml_too_deep` did (the DOCTYPE entity
count included) and, in the same walk, notes every `data:` attribute value, answering the text
with those values left out, their quotes kept. The prepass and `fx::blends` search that text;
the edits are still made in the source. A data URL can hold no tag, attribute name, selector or
CSS declaration, and everything else, a `style="display:none"` included, is kept, so what is
found is what was found before.

`vectorcraft-cli perf --deep` gains a tracked row, "import SVG, one embedded PNG", beside the
export row it had.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-23 01:01:27 +07:00
dotCooCoo 48792842ec Package: reads placed pages and document previews only for the options that use them
With Copy Links on and Copy Fonts off, File › Package still read every
placed PDF or .ai page to find the fonts of its type, and then dropped
them. With Copy Links on and Relink off, it still read the preview and
the PDF of each placed .vectorcraft document, which only the rewritten
copy uses.

Package now reads the placed pages for their fonts only with Copy Fonts
on, and it reads the preview and the PDF only with Relink on. With Copy
Fonts off, packaging a document that places the 60 pages of a PDF took
about 390 ms in a release build and now takes about 20 ms. The package
it writes is the same.
2026-10-23 01:01:26 +07:00
Mirko Kerezovic 97c173beb9 Handle PDF title edge cases 2026-10-23 01:01:26 +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
BFlat 524ecee6ca Merge pull request #3561 from lehni/feature/emoji-bitmaps
Type: colour emoji fonts with bitmap glyphs draw and export
2026-10-23 00:59:15 +07:00
BFlat 7fd4b33dc5 Merge pull request #3670 from dotCooCoo/fix/batch-revert-keeps-the-view-artboard
Artboards: an action or Batch… that reverts or switches documents keeps the active artboard
2026-10-23 00:46:58 +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
BFlat 9b2173611f Merge pull request #2975 from nrq6993-a11y/stream/vc-feat3-g12-low-res-proxy-eps
G12: honor Use Low Resolution Proxy for Linked EPS (#394)
2026-10-23 00:37:02 +07:00
BFlat b50fbc08cf Merge pull request #3668 from Taha-Mahmoodi/fix/dither-followups
Gradients: dithered fills sample only what shows; review follow-ups to #3637
2026-10-23 00:23:42 +07:00
dotCooCoo 44c306f142 Artboards: an action or Batch… that reverts or switches documents keeps the active artboard
With the Artboard tool, File › Revert kept the active artboard, but a
command.batch with a file.revert step made artboard 1 the active one.
A batch that switched to another document and back, and the Actions
panel menu's Batch… over a folder whose files it closes or over data
sets with a Revert step, did the same. Each of them starts the tool
afresh and ends in the document it started in, and the app moved the
view to the fresh tool's artboard 1. Only a plain file.revert was
handled, by its command id.

The engine now counts the times switching or closing documents, or
replacing the active one, starts the tool afresh (Session::tool_resets),
and records whether the tool's options were set after that
(Session::tool_set_since_reset). When the count changes during a
command and nothing set the tool's options after it, the app starts
the tool on the active artboard, as it does when a command chooses
another document. When a later step chose the tool's artboards (a
Paste or Cut after the revert), the artboard the tool then has active
becomes the active one.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-23 00:20:54 +07:00
bflatastic 30360a67a0 G12: honor Use Low Resolution Proxy for Linked EPS (#394)
Squashed from #2975 onto main.
2026-10-23 00:14:52 +07:00
BFlat 24fadbd322 Merge pull request #3664 from dotCooCoo/feat/shape-builder-artwork-style-rule
Shape Builder: Pick Color From Artwork takes the look where the drag starts, else where it ends
2026-10-23 00:11:07 +07:00
BFlat 72dce93463 Merge pull request #3662 from nrq6993-a11y/stream/vc-featD-g8-art-style-filter-cap
G8: Regression test for art style filter cap
2026-10-23 00:10:48 +07:00
BFlat 40e40bf8cb Merge pull request #3660 from nrq6993-a11y/stream/vc-issue-3635-linux-maximize
Restore and remember maximized window state
2026-10-23 00:10:28 +07:00
BFlat ab330e4adc Merge pull request #3649 from nrq6993-a11y/stream/vc-feat3-g4-mixed-hyphenation-dialog
G4: Mixed Hyphenation dialog fields
2026-10-23 00:10:09 +07:00
BFlat dc38eb6bee Merge pull request #3648 from nrq6993-a11y/stream/vc-feat1-g4-mixed-paragraph-direction
G4: Mixed paragraph direction leaves direction buttons unselected
2026-10-23 00:09:51 +07:00
Taha-Mahmoodi 6b10b159b9 Gradients: dithered type in PDF keeps its resolution when scaled and stays searchable
Review follow-up (CodeRabbit): a gradient drawn as pixels was sized from its
area in the space it is drawn in, so type scaled 4 times got a quarter of the
raster effects resolution; the image is now sized from the area on the page,
within the same pixel caps. And type whose fill is drawn as art (a dithered
or freeform gradient's image, a pattern) went out as outlines only; its
characters are now also written as invisible text (rendering mode 3), as
unpainted type already is, so they can be selected and searched.
2026-10-23 00:08:53 +07:00
Taha-Mahmoodi 7c9edfaf7f Gradients: an outlined stroke leaves dither behind, and paint.setStroke refuses it
Review follow-up (CodeRabbit): paint.setStroke kept `dither: true` from its
gradient, and a stroke can hold dither anyway (swapped from a fill). Outline
Stroke and appearance expansion copied the stroke's paint into the outline's
fill, which then drew dithered although the stroke never did. The outline's
fill now goes without dither (Paint::stroke_as_fill), and paint.setStroke
refuses a gradient with `dither: true`, as paint.editGradient does.
2026-10-23 00:08:53 +07:00
Taha-Mahmoodi 159e20053c Gradients: a dithered gradient without stops draws nothing, as an undithered one does
Review follow-up (CodeRabbit): a dithered fill skipped set_paint's guard for
a gradient without stops and sampled a transparent grid to draw nothing. It
now takes the same no-fill path.
2026-10-23 00:08:52 +07:00
Taha-Mahmoodi 4062e74ff3 Gradients: a dithered fill samples only the part of its grid the view shows
Review follow-up (CodeRabbit): a large dithered gradient mostly off-screen
sampled its whole grid (up to 4096 x 4096 cells, 64 MiB) to draw a small
visible part, and did it again on each zoom step. The renderer now samples
only the cells the render target can show, a cell more each way for the
bilinear mix, snapped out to 256-cell tiles so a pan within them reuses the
grid. Cells keep their place in the whole grid, so the gradient and its
noise are the same pixels as before (GradientPaint::pixels_within).
2026-10-23 00:08:52 +07:00
dotCooCoo fffc8418d9 Shape Builder: Pick Color From Artwork takes the look where the drag starts, else where it ends
With Pick Color From Artwork, a Shape Builder merge took the look of
the front-most object over the first covered region the drag touched,
and the current fill when the drag touched only areas no object
covers. The reference app's combine page gives the look of the object
where the drag starts, else of the object where it ends, else of the
topmost selected object.

shapeBuilder.merge now follows that rule: the merged shape takes the
appearance, opacity and blending mode of the front-most of the objects
it is given (the selection by default) under the first point, else of
the one under the last point, else of the front-most of those objects.
A drag from a hole across a gray bar into a red square now gives a red
shape, where it gave the bar's look. A click in an area no selected
object covers, such as a hole that gray bars enclose or a triangle
that lines enclose, takes the front-most selected object's look
instead of the current fill. Pick Color From Color Swatches works as
before. The tool already sends the press point first and the release
point last.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-22 23:56:01 +07:00
Cursor Agentandnrq6993-a11y 00f557e02e G8: regression test for art style filter cap
## Summary
Illustrator `%AI9` art styles already stop reading filters after 4096 entries so
hostile styles cannot grow without bound. This adds a regression test that
importing a style with more than `MAX_FILTERS` basic filters finishes with a
bounded effect list, matching the existing hardening behaviour.

## Tests
- `art_styles_ignore_filters_beyond_the_cap` in `crates/eps/src/import/ai/style.rs`

Part of gap G8

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 23:56:01 +07:00
Cursor Agentandnrq6993-a11y 0490ca0788 fix(linux): restore and remember maximized window state
## Summary

On Linux, asking winit to maximize before the window is mapped often does
nothing, and egui sometimes omits the maximized viewport flag while the
window manager still has the window maximized. Defer maximize until the
first frames after startup, and read maximized state from winit when
saving geometry for the next launch.

## Tests

cargo test -p vectorcraft window::
cargo clippy -p vectorcraft --all-targets -- -D warnings

Closes storytold/vectorcraft#3635

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 23:56:01 +07:00
BFlat 45f6e99eb1 Merge pull request #3647 from dotCooCoo/fix/package-names-a-rejected-path-once
Package: a file the writer refuses is named once in the error
2026-10-22 23:54:23 +07:00
bflatastic 329916c2b8 G4: Mixed Hyphenation dialog fields
Squashed from #3649 onto main.
2026-10-22 23:46:14 +07:00
Cursor Agentandnrq6993-a11y 2f3ccb7b2c G4: mixed paragraph direction leaves direction buttons unselected
## Summary
When the Paragraph panel acts on more than one paragraph whose displayed
direction differs (explicit LTR/RTL or Auto resolved from content), neither
Left-to-Right nor Right-to-Left is highlighted, matching alignment and the
other mixed-value paragraph controls in Adobe Illustrator.

## Tests
- `paragraph_mixed_direction_buttons_show_neither` in `panels/paragraph.rs`

Part of gap G4

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 23:45:28 +07:00
BFlat dbf6258eae Merge pull request #3641 from pat26-dotcom/fix/3623-pdf-import-word-gap
Fix #3623: a word gap between two sizes reads as a space in PDF import
2026-10-22 23:41:26 +07:00
dotCooCoo 4a4c8c9d49 Package: a file the writer refuses is named once in the error
File › Package… put the file's path in front of every error its writer
returned. A refusal from the automation write root already names the
path, so the dialog's error and the status bar named the file twice,
for example on Windows for a document named aux:
`<path>: automation path rejected: reserved device names are not
allowed: <path>`.

The dialog now keeps a writer error that already contains the path as
it is, and puts the path in front of one that does not, such as the
desktop's "Access is denied. (os error 5)".
2026-10-22 23:36:57 +07:00
BFlat 6e091a33f5 Merge pull request #3636 from MiQ71/fix-3447-spiral-dialog
Fix Spiral tool click options dialog
2026-10-22 23:27:46 +07:00
Patrick LenzenandClaude Opus 5.5 6903bb7ef8 Fix #3623: a word gap between two sizes reads as a space in PDF import
PDF import measured each gap between two glyphs against the larger of
the line's first glyph size and the next glyph's. A word set beside a
much larger one, apart by positioning and no space glyph (a browser's
margin between two sizes), stayed under a fifth of that em: "≤ 4" at
20 pt and "hours" at 8 pt, 3.4 pt apart, came in as "≤ 4hours".

A gap is now measured against the smaller of the two glyphs beside it.
Lines in one size read their gaps as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 23:14:35 +07:00
BFlat b35a6b1573 Merge pull request #3638 from dotCooCoo/fix/cmd-enter-shortcut-skips-the-tool-enter
Shortcuts: a command on Cmd+Enter or Cmd+Escape runs without the tool's Enter or Escape
2026-10-22 23:11:43 +07:00
bflatastic a99d81e9aa Fix Spiral tool click options dialog
Squashed from #3636 onto main.
2026-10-22 22:57:26 +07:00
BFlat d73946ba46 Merge pull request #3637 from Taha-Mahmoodi/feat/gradient-dither
Gradients: Dither in the Gradient panel draws a gradient fill with fine noise that hides banding
2026-10-22 22:56:41 +07:00
dotCooCoo bd12b48e02 Shortcuts: a command on Cmd+Enter or Cmd+Escape runs without the tool's Enter or Escape
Enter and Escape went to the active tool whatever modifiers were held,
and the command bound to the chord then ran as well. With a command
bound to Cmd+Enter, Cmd+Enter ended the Pen tool's path, opened the
Move dialog with the Selection tool, or typed a line break while the
Type tool edited text, and then ran the command. With a command bound
to Cmd+Escape, Cmd+Escape ended the Pen path, the Type tool's text
editing or isolation mode before the command ran.

A press of Enter or Escape whose chord a command is bound to, matched as
the shortcut dispatcher matches it, now runs only that command, also
when the tool claims the key. While the Type tool edits text, only Cmd
chords count. While a tool is in the middle of an interaction (a drag,
or between the clicks of a tool such as Blend or Dimension), and for
plain Enter and Escape and chords that no command is bound to, the key
goes to the tool as before.
2026-10-22 22:44:58 +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 a1d8952bc8 Gradients: a linear or radial gradient can be drawn as pixels, and SVG and PDF write such a gradient as an image
Shared groundwork for gradient options SVG and PDF can't express (Illustrator
30's dither and perceptual gradients): GradientPaint::pixels samples a placed
linear or radial gradient on a grid (Ramp: its stops, midpoints as stops, mixed
as the renderer mixes them), and draws_as_pixels says which gradients are drawn
that way (freeform ones, so far). The SVG and PDF writers' freeform-gradient
image path takes any such gradient, a linear or radial one at the document's
raster effects resolution. No change in output yet.
2026-10-22 22:38:41 +07:00
Taha-Mahmoodi 4bb6f32911 Gradients: built with Gradient::new, so a new option touches one place 2026-10-22 22:38:41 +07:00
BFlat 30d1948f51 Merge pull request #3496 from dotCooCoo/feat/place-links-pdf-and-ai
Place: Link keeps a placed PDF or .ai linked to its file
2026-10-22 22:09:29 +07:00
dotCooCoo c4ac0c5df5 Package: copies the fonts of the type on a linked PDF or .ai page
A PDF or .ai page placed linked is a placed document, and File ›
Package listed only the fonts of the document's own type, so the fonts
of the page's type were left out of Fonts/ where an embedded page's
were copied. Package now reads each linked page a document places and
adds the fonts of its type, also when another document placing the same
file copied it first, and also with copyLinks off.
2026-10-22 21:52:24 +07:00