Commit Graph
2926 Commits
Author SHA1 Message Date
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 3d24c663d2 Fix #3233: dimensions are redrawn in the colour mode after Document Color Mode and on open
Follow-up to #3702 (CodeRabbit): dimensions are only redrawn when their
recipe changes. Document Color Mode converted their art through the colour
profile, so black became a four-ink C67 M67 Y67 K100 again, and CMYK files
saved before #3702 kept RGB dimension art.

dimension::refresh now also redraws when the document's colour mode
changes. dimension::conform, run when a document is opened or reverted
(Session::add_document, replace_document), redraws a dimension whose art
has colours in the wrong model; one that can't be redrawn keeps its art.

dimension_colours_follow_the_document_colour_model covers RGB to CMYK and
back, and a CMYK document with RGB dimension art being opened. Without the
change it finds C0.67 M0.67 Y0.67 K1 after Document Color Mode.
2026-10-23 04:26:05 +07:00
dotCooCoo 6da6b4440f Shape Builder: a split stroke keeps its path's direction and gradients
With In Merge Mode, Clicking Stroke Splits the Path on, the run a click
split off could run against its path. A click on a rectangle's top side
gave a side from (100, 0) to (0, 0) on a rectangle that runs from
(0, 0) to (100, 0) there, so arrowheads and other settings that follow
a stroke's direction moved to the other end. The run now goes the way
the path goes.

The two paths of a split, and what an Alt-click or a merge leaves of a
path, had a gradient fitted to the path's bounds fitted again to their
own bounds. They now keep it where it was on the path.

A run that left 0.05 pt or less of a path still split it, into the
run and a speck. Such a path now does not split, and the tool shows
no scissors over it.

Docs: docs/mcp.md (Shape Builder Tool Options).
2026-10-23 04:14:38 +07:00
Taha-Mahmoodi 087e38aafc Render: a dashed brush stroke keeps the layered path for object opacity
Follow-up to #3697 (CodeRabbit): a brush stroke is drawn as separate
pieces (draw_brush), so folding object opacity into its colour isn't exact.
A non-dashed brush stroke already counted as one paint before #3697; to
keep that change's scope, a dashed stroke counts as one paint only when it
has no brush. dashed_stroke_opacity_folds_into_its_colour checks that a
dashed brush stroke isn't folded; it fails without the change.
2026-10-23 04:14:37 +07:00
1e45fe99f7 i18n(ka): Relative Colorimetric is ფარდობითი კოლორიმეტრიული
The adjective from კოლორიმეტრია is კოლორიმეტრიული; ფარდობითი matches PhotoCraft
(storytold/photocraft#5845, from a review by supersfera-online).

Co-authored-by: supersfera <223132313+supersfera-online@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-23 04:01:58 +07:00
BFlat b3177919a9 Merge pull request #3702 from Taha-Mahmoodi/fix/3233-dimension-cmyk
Fix #3233: in a CMYK document dimension lines and labels are CMYK, black on the K plate only
2026-10-23 03:47:44 +07:00
Taha-Mahmoodi 866bf61116 Fix #3233: in a CMYK document dimension lines and labels are CMYK, black on the K plate only 2026-10-23 03:35:07 +07:00
skylar taylor-barrick 2e142583b3 #2278: test that the Properties flip buttons keep the reference app's names
The Properties panel's flip buttons had been renamed Flip Horizontal and Flip Vertical, which are the Transform panel menu's names. The test lays out the Transform section for a selected rectangle and reads the screen-reader names of its buttons: Flip Along Horizontal Axis and Flip Along Vertical Axis must be there and the old names must not. With the old labels the first is missing.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 8abb1d1c71 #1807: test that a zstd block larger than its frame's window is refused
ruzstd 0.9.0 let a block decompress to more than its frame's window allows (RUSTSEC-2026-0344). The test swaps a hand-built Zstandard frame with a 1 KiB window and one 100,000-byte RLE block into an Affinity container entry and checks that reading the entry fails. With ruzstd pinned back to 0.9.0 the block is decompressed and the read succeeds.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 02f5bc8666 #3533: test that a popover stays open through its anchor click and a panel drag
The Fill/Stroke chip's popover closed on the click that finished a drag between its proxies. The test opens a popover and checks the three outcomes: a click elsewhere closes it, a click on its own anchor that does not toggle it keeps it open, and a click elsewhere during a panel drag keeps it open. Without the fix both of the last two close it.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick ae6cf1a729 #2083: test that panel menu rows light up under the pointer
The rows of the panels' menus did not light up under the pointer as the menu bar's items do, because they were drawn with no frame at all. The test hovers a menu_item_name row and checks that a filled rectangle appears behind it, and that none is there at rest. With frame(false) the hovered row stays unfilled.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick a65cae367f #1506: test that sliders show their rail filled up to the handle
The theme's widget fill matched the dialog background, so a slider in a dialog showed only its handle. The test draws widgets::slider in all four brightnesses and checks that a rail in the buttons' border colour and an accent fill from the rail's start up to the handle are painted. With a plain egui slider neither is drawn, only two rectangles in the panel's button fill.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick f39c9544eb #1490: test that selected text in value fields is in the strong text colour
Selected text in a value field was drawn in the accent on a selection tinted with the same accent, hard to read in every brightness. The tests focus a panel number field and a dialog text field, select their text and read the colours of the glyphs drawn, in all four brightnesses: all must be the strong text colour. Without strong_selected_text the glyphs come out in the accent.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick b5a99542eb #916: test that a spinner field reads as one box in three parts
A number field with a spinner and preset chevron drew as three separate boxes, each joint with two borders. The test lays out a spin field and checks that its three boxes are the row's height and that neighbours overlap by one point so they share a border. Without the overlap (item spacing 0) the boxes touch edge to edge instead.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 888af39881 #815: test that the toolbar's scroll bar stays a slim line
The toolbar's and the dock icon column's scroll bar widened to 10 points while the pointer was on it, covering the group labels and the tool icons' right edge. The test lays out a scrolling strip, rests the pointer on its bar and measures the widest filled rectangle drawn: at most 4 points. Without the slim scroll style it measures 10 points.
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 3f74e38fea #871: test that the Artboard tool names its active artboard once
With the Artboard tool the active artboard's name was drawn twice: by the canvas, as for every artboard, and by the tool, a little higher. The test draws a frame with two artboards and checks that each name is painted once and that the active artboard's sits where the other's does. It fails without the canvas skipping the tool-labelled artboard (two labels for the active one) and without the tool's label position (8 points to the right and 11 higher than the other name).
2026-10-23 03:33:30 +07:00
skylar taylor-barrick 548fad4b59 #480: test that the Add and Delete Anchor Point tool icons are the Pen's icon with a badge
The Add and Delete Anchor Point tools' icons were drawn apart from the Pen tool's, so the three didn't read as one family. The test checks that both tools' icons contain every shape of the Pen tool's icon plus a badge of their own, and that plus and minus differ. With the tool mapping pointing back at the old dc-pen-add and dc-pen-delete icons it fails: the Add tool's icon lacks the Pen's outline.
2026-10-23 03:33:30 +07:00
Taha-Mahmoodi 32072c77fa Fix #2788: a dashed stroke with object opacity folds the opacity into its colour instead of compositing a canvas-wide layer 2026-10-23 03:22:55 +07:00
Gert Pellin 368fa3def2 Paint: a fill or stroke given to a compound path's member paints the compound path (#3692) 2026-10-23 03:22:54 +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
BFlat 1673b3b747 Merge pull request #2942 from nrq6993-a11y/stream/vc-feat3-g12-real-time-drawing
Honor Preferences › Performance › Real-time Drawing and Editing during tool drags
2026-10-23 02:44:15 +07:00
BFlat 74c9226c3f Merge pull request #3690 from Silvertree2010/fix/h1206-unit-parse-rejects-an-explicit-l
Accept an explicit leading plus sign in Unit::parse
2026-10-23 02:31:38 +07:00
bflatastic c1f96cc6f8 Honor Preferences › Performance › Real-time Drawing and Editing during tool drags
Squashed from #2942 onto main.
2026-10-23 02:22:09 +07:00
BFlat f44dd739b2 Merge pull request #3688 from addibble/perf/svg-import-css-data-urls
SVG import: leave out a data URL inside url(…) too, and jump over each one
2026-10-23 02:18:29 +07:00
NetterSilverandClaude Haiku 5.5 56d34d4ced Accept an explicit leading plus sign in Unit::parse
Found while testing crates/doc/src/lib.rs:379 (number_unit, reached from parse_measure at :374)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 02:10:57 +07:00
Drew DibbleandClaude Opus 5.5 2d85603bcc SVG import: leave out the data URL of a url(…) in any attribute value too
A follow-up to #3677. A data URL in CSS, `style="fill:url(data:…)"` or a paint's
`url(…)`, stayed in the text the prepass searches, since only a value beginning with `data:`
was left out (CodeRabbit's review of #3677). Each `url(` in an element's quoted value whose URL
begins with `data:` is now left out up to the byte that closes it, its quote or `)`, so what
follows it, `display:none` say, stays. A `url(` that does not close inside its value is kept
whole: where the end is unclear, less is left out, never more.

The scan now jumps over a data URL with one search for the byte that ends it, instead of
stepping through it a byte at a time. The markers' capacity is a saturating sum, so no range can
panic it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:04:46 +07:00
ham9zah 1abc5d4066 Import: type on a closed path keeps its wrapped end bracket
Round a closed path the editing data's text document writes the end of
the type's range unwrapped, past the last segment (the start plus the
run), or wrapped the other way. The importer clamped it to the path's
end, so the type kept only the stretch from its start bracket to the
path's end and overflowed. Both brackets are now wrapped onto the loop
(non-finite values fall back to the start); an open path is clamped as
before.

The object's own path that then replaces the frame is written open,
ending on its start: such a path is now closed where its ends meet, so
the span is measured round the loop.

Hand-written tests: an unwrapped, a reversed and a negative range on a
closed circle, a range inside it, an exact full turn that fits a short
line, an open path still clamped, and a path ending on its start closed.
2026-10-23 01:53:47 +07:00
makesomethingshit 6a394c3a6f #3607: follow the active artboard with every tool 2026-10-23 01:53:47 +07:00
makesomethingshit 4d9749f4c7 #3607: open Save for Web on the active artboard 2026-10-23 01:53:35 +07:00
bflatastic efe2429618 Shape Builder: in merge mode, a click on a stroke splits the path there
Squashed from #3665 onto main.
2026-10-23 01:53:34 +07:00
Taha-Mahmoodi 8924b24e94 Gradients: Expand to a gradient mesh keeps a Perceptual gradient's colour curve
Review follow-up (CodeRabbit): a linear gradient expands to a mesh with a
column at each stop, and the mesh mixes linearly between columns, so a
Perceptual blue -> yellow came out as the grey RGB mix in the middle.
`breaks` now also takes a column at each stop the Perceptual expansion adds
(Gradient::expanded), so the mesh follows the OKLab curve; Classic is
unchanged. the_mesh_option_makes_a_mesh_that_paints_the_gradient gains a
Perceptual case; without the change 1812 pixels differ from the gradient.
2026-10-23 01:53:30 +07:00
Taha-Mahmoodi 6fd1f566e1 Gradients: the Perceptual test describes the method in our own words
Review follow-up (CodeRabbit): the test's doc comment quoted a phrase from
the public help page. AGENTS.md allows feature names and a cited URL, not
copied wording; the comment now describes the behaviour itself and keeps
the URL.
2026-10-23 01:53:30 +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
Taha-Mahmoodi 2bad646821 Gradients: a Perceptual gradient mixes its colours in OKLab, and SVG and PDF write it as an image
Illustrator 30.0's gradient Method, from the public documentation
(https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-perceptual-interpolation-in-gradients.html):
Classic or Perceptual; perceptual interpolation matches colour transitions to
how the eye perceives differences between colours, and perceptual gradients
are rasterised when saved as PDF or SVG.

- Gradient gains `method` (Classic | Perceptual; serde default Classic,
  written only when Perceptual, so older files open as Classic and Classic
  files are written as before). The colour crate's OKLab helpers open up for it.
- Perceptual mixes neighbouring stop colours in OKLab (sample, and
  sample_keep_model for the stroke meshes, Expand and blends); opacity stays
  linear. Renderers that mix linearly get stops on the perceptual curve,
  placed by halving each half segment where a straight line strays more than
  0.4 of a colour level (a nearly straight segment needs none). Classic
  gradients expand exactly as before.
- draws_as_pixels includes Perceptual gradients, so SVG and PDF write a
  Perceptual fill or stroke as an image at the document's raster effects
  resolution, clipped to what it paints. A gradient along or across a stroke
  stays slices (PDF no longer draws it as a within-stroke image).
- With Dither: a dithered Perceptual fill is the perceptual mix with dither's
  noise (the canvas's dithered grids are keyed by method too); SVG and PDF
  write it as an image with the noise, and a dithered Perceptual stroke as an
  image without it, as the canvas draws it (GradientPaint::pixels, SVG's
  raster_paint and PDF's area are told whether they paint a fill).
2026-10-23 01:53:29 +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
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 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