2289 Commits

Author SHA1 Message Date
Don Omondi f7135db6e9 Drop Shadow and Outer Glow: spinner arrows step Opacity, the offsets, Blur and Darkness (#5367, #5368) 2026-11-06 17:09:24 +07:00
Don Omondi f08381df8d SVG import: filter and mask regions clip what they declare (#5876, #5875)
Squashed from #5913 onto main.
2026-11-06 16:39:04 +07:00
Don Omondi 74b5966bbd Stroke Weight Up and Down: Cmd/Ctrl+Alt+. and , step the selected strokes' weight (#5797) 2026-11-06 16:19:37 +07:00
Don Omondi 41ceaadc7e SVG import: Gaussian blurs keep color-interpolation-filters; Gaussian Blur can blur in linear light (#5877) 2026-11-06 16:05:55 +07:00
Don Omondi dbbe8a9298 PDF and .ai export: objects' URLs become clickable links (#5785)
Each visible object with a URL (Attributes panel) is written as a Link
annotation with a URI action over its bounds on the page, trimmed to its
clipping groups. Hidden objects, non-printing layers and printed sheets get
none; PDF/X files leave them out with a warning. URIs are percent-encoded
to 7-bit ASCII.
2026-11-06 15:48:02 +07:00
dotCooCoo d58d47312f Legacy .ai export: black-and-white colorized images save as image masks
A colorized image whose pixels are all black or white (or clear) is
written as a 1-bit image mask (ImageMask 1): its color as the fill, a 0
bit for each black pixel and a 1 bit for each white or clear one. The
legacy reader opens it colorized again with the same clear pixels, so
the engine no longer flattens or omits it. Other colorized images are
written with their color in their pixels, over white, with the note
for transparent images.
2026-11-06 15:33:35 +07:00
Don Omondi 427ec0017c SVG import: gradients with color-interpolation="linearRGB" mix their stops in linear light (#5878) 2026-11-06 15:33:02 +07:00
dotCooCoo 968bf1dc48 .ai import: a pattern or color named as another swatch keeps its swatch
Squashed from #5903 onto main.
2026-11-06 15:33:00 +07:00
dotCooCoo 8f81e142a5 Reject invalid Tab Stop string parameters 2026-11-06 15:20:17 +07:00
Don Omondi 00b09002cf Layers: Rename opens the selected layer, group or object's name field (#5324) 2026-11-06 15:14:20 +07:00
Adithya Shankaran a455e72bf8 Automation roots: refuse COM¹ to LPT³ as Windows device names 2026-11-06 14:34:58 +07:00
dotCooCoo def29d0f97 Docs: .ai symbols are read with the file's layers
A .ai that comes in as its page or PDF part (editing data with no layers, layers that can't be read, or a page fallback) gets none of the editing data's symbols: layered_ai_from fills symbols only from build_from. The editing-data paragraph now says so.
2026-11-06 14:04:04 +07:00
dotCooCoo b5da39d7b5 Commands: true/false flags reject other values everywhere
The remaining flags under crates/engine/src/cmd that read a boolean with
get(..).and_then(Value::as_bool).unwrap_or(..) now go through switch_or,
so a value such as "false" or 1 is an error instead of the default.
Omitting a flag or giving null keeps the old behavior.

- effect.move: copy
- aligned on the appearance.*, stroke.set, transparency.set and effect.*
  commands, checked before any early return
- transparency.viewOpacityMask: on
- view.printTiling: on
- plugin.install: replace and allowHeavy, checked for every plug-in
- edit.clear: guides, and path.deleteAnchors' read of it
- text.create and text.createInPath: vertical (already refused by
  check_switches, so their behavior is unchanged)
2026-11-06 13:47:16 +07:00
dotCooCoo 31456a0cec .ai import: a file whose editing data has no layers also opens on its active artboard 2026-11-06 13:47:16 +07:00
dotCooCoo aac21aa076 .ai import: legacy type keeps one copy of tab stops its paragraphs share 2026-11-06 13:22:36 +07:00
dotCooCoo e6819a86f0 Rendering: open width-profile strokes outline only near the view 2026-11-06 12:42:33 +07:00
ruchirguitar 70f798b71d i18n: add Hindi interface and message catalog 2026-11-06 12:40:10 +07:00
dotCooCoo 2fa3276789 Selection: empty-canvas hover skips distant widgets in large selections 2026-11-06 12:06:27 +07:00
dotCooCoo 0473fcad58 Docs: the .ai notes no longer list guides as a known failure
The Known failures bullet in docs/file-format-parity.md kept stale copies
of its earlier wording after several merges: guides were still listed as
lost although they open as ruler guides since #5057, and the same clauses
about #868, #1027 and #758 repeated several times. The bullet now states
each fact once, and keeps every legacy .ai reading note it carried.
2026-11-06 12:04:14 +07:00
dotCooCoo 64c38d29c7 Reuse hit result for Direct Selection cursor 2026-11-06 11:31:21 +07:00
dotCooCoo 8a730bab6b Reject invalid New Art Basic parameters 2026-11-06 11:15:35 +07:00
dotCooCoo 0416f115e0 Speed up hover hits on long plain paths 2026-11-06 11:00:14 +07:00
dotCooCoo b26bd3d472 Reject invalid Guide Lock parameters 2026-11-06 10:41:08 +07:00
dotCooCoo 49583ef632 .ai import: symbol, pattern and brush definitions share the file's blend and envelope limits
Each definition's reader started with fresh blend layout and envelope
check budgets, so a file with many symbols, patterns or brush art
sections got those limits again for every definition. The reader of a
definition now takes the import's remaining budgets and hands back what
is left after reading, whether the definition is kept or not.
2026-11-06 10:28:08 +07:00
dotCooCoo 398131b9cc Reject invalid Transparency Grid parameters 2026-11-06 10:12:42 +07:00
dotCooCoo 48a3710fae Reuse selected object locations for large transforms 2026-11-06 09:58:43 +07:00
dotCooCoo b4a4f2aa4f Reject invalid Slice toggle parameters 2026-11-06 09:42:35 +07:00
dotCooCoo 25da816a08 Rebuild fitted text geometry after scaling 2026-11-06 09:37:22 +07:00
dotCooCoo a723f7359a Reuse text outlines across moves 2026-11-06 08:51:36 +07:00
Don Omondi ae3490d3ba Properties: Edit Artboards shows a Transform section above the artboard options (#5219)
With the Artboard tool, the Properties panel now starts with a Transform
section: the reference point shared with the Transform panel and the
Control bar, the active artboard's X and Y (of that point), W and H with
the Constrain Width and Height Proportions link, its preset and
orientation, and Move and Scale Artwork with Artboard. The Artboards
section follows with the name, Artboard Options and Fill. A new W or H
now keeps the reference point in place, as in the Transform panel.
2026-11-06 08:34:46 +07:00
Don Omondi e1439bfe33 Scrubby labels: the Control bar's Stroke: and Opacity: links scrub their fields (#4088)
The Control bar's Stroke: and Opacity: and the Properties panel's Stroke
are links to their panels, so a drag on them did nothing while every
other field label scrubbed its field. A drag on such a link now scrubs
the field after it (same steps and modifiers, one undo step, Escape
cancels); a click still opens the panel, and the link's text is not
selected by the drag.
2026-11-06 08:11:10 +07:00
Don Omondi 97697420f9 Variables: Load Variable Library keeps a bound text variable the library declares (#4136)
Loading a variable library deleted every variable of the document with its
bindings, including a text variable of the same name that the library
declares. Text made dynamic and then given a library's variable name lost its
binding, so selecting the library's data sets changed nothing. Such a
variable now stays bound, and the library's data sets change its text; other
variables are deleted as before.
2026-11-06 07:45:21 +07:00
Don Omondi 283c0d12bb Effects: an image's Inner Glow follows its visible pixels, not its frame (#5308)
Inner Glow clipped to the art's outline and bled in from outside it; for a
placed image that outline is the frame, so a PNG with transparent areas
glowed along the frame's edge, over pixels that don't show. For an image the
glow is now cast by its pixels' alpha (everything around them, blurred, or
the pixels themselves with Center) and kept to where the image shows, as
much as it shows.
2026-11-06 07:25:10 +07:00
Don Omondi 1f7f341c21 Properties: Quick Actions has Repeat Options for a selected repeat (#5181)
One selected radial, grid or mirror repeat (or one object of its source
art in isolation mode) shows a Repeat Options button in the Properties
panel's Quick Actions. It runs Object › Repeat › Options, opening the
Repeat Options dialog with the repeat's current values.
2026-11-06 07:23:39 +07:00
Don Omondi f987c2bbb0 Intertwine: the object brought over leaves no hairline of the one below (#5337)
The area an Intertwine member is drawn over another in was exactly the
overlap face, so its edge lay on the edge of the member below. Both edges
were anti-aliased, and the member below showed through as a thin line along
them. The area now reaches 2 pt past the face into the parts where only the
raised member paints (where drawing it again changes nothing), so its edge
lies inside that member.
2026-11-06 07:23:35 +07:00
Simon Hull c15383c510 Images: an image zoomed in on has its colours made only for the tiles in view (#4155)
From about 50% zoom on the file of #4155, the pixels drawn are the full 141-megapixel photo,
and in a CMYK document they were separated into ink planes (or colorized, or proofed) whole
for every view: 8 to 9 s of white canvas a zoom step.

When the pixels drawn are larger than a 1,024-pixel tile, their colours are made again for the
frame and part of them is out of view, only the tiles in view are made, each kept under its own
key (`ImageKey::tile`), and drawn together as one pixmap so their edges don't show. The colours
are made pixel by pixel, so the result is the same as for the whole image. CMYK images painting
their own inks are still made whole.

Zooming in to 50%, 100% and 200% on that file went from 9.0, 8.6 and 8.7 s to 0.78, 0.05 and
0.05 s (M3 Pro, one run each), with the same pixels on screen.
2026-11-06 07:23:32 +07:00
Don Omondi dfe628e906 Blend spine: Shift-click picks several spine points, which drag together (#5285)
Direct Selection kept only one picked point of a blend's spine. Shift-click
now adds a point of the same spine or takes it away, and dragging any picked
point moves them all. object.blend.spine.moveAnchor takes `with`, the other
points to move by as much, with the key objects on them, in one undo step.
2026-11-06 07:23:19 +07:00
Don Omondi 483de72dbb Direct Selection: Cmd/Ctrl held lends the Selection tool and shows its bounding box (#3901)
Squashed from #5851 onto main.
2026-11-06 05:33:00 +07:00
Don Omondi b976de560d Join tool: scrubbing the ends of two crossing paths trims them and joins the paths at the crossing (#5844)
The Join tool drew a segment between the open ends of two crossing paths
instead of trimming what overhangs the crossing. path.joinScrub now looks for
two crossing open subpaths whose overhanging ends the scrub runs over, cuts
both at the crossing and joins the kept parts there with a corner, in one undo
step. Without a crossing the endpoint join is unchanged.
2026-11-06 05:32:24 +07:00
Simon Hull af31e1464c Canvas: a document's first frame renders on the render worker (#4155)
A document's first frame (opened, or its tab chosen) rendered on the UI thread: only a
document with a frame already shown sent its frames to the worker. A heavy file froze the
window until it was drawn, 8.7 s for the file of #4155 and 1.4 s for a 50,000-path SVG.

The first frame now goes to the worker too, and the window shows the document's paper and
artboards until it lands. The UI thread waits for it up to FIRST_FRAME_GRACE (16 ms) when it
is queued (`Worker::wait`; `submit` says whether it queued), so a light document's art still
shows in the same frame. The longest UI-thread stall opening those files is now 64 ms and
62 ms (M3 Pro, one run each). The web build, which has no worker, is unchanged.
2026-11-06 04:55:02 +07:00
Zinadin Zidan d04585f6d2 Control bar: Paragraph options while type is selected (#5372)
With type selected the Control bar now shows a "Paragraph:" link, which
opens the Paragraph panel in a popover as Character: does, and Align
left/center/right. The bar now trims strictly from its right end as it
narrows: X/Y/W/H, Distribute, object Align, area type's vertical alignment,
then the Paragraph buttons, then the link, each group measured. That also
stops Align and Distribute running past the bar's edge for other
selections. On a 1440 pt bar with one point type object the buttons show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 04:54:35 +07:00
Simon Hull 8147d33f56 Images: a reduced image is shrunk before it is separated into ink planes (#4155)
With image mips on (the canvas, navigator and layer thumbnails), an image was colorized,
separated into the frame's ink plane and shown through Separations Preview's plates at its
full size, and only then shrunk to the mip drawn. The 141-megapixel image of #4155 made
1.6 GB of planes a frame, more than the image store holds, so every frame made them again.

The mip is now picked first and everything else is made from it, under the keys `kept_mip`
and `nearest_kept` already look up. A CMYK image's own inks are halved with its pixels
(`ink::key_inks`, `images::half_inks`), so it still paints them rather than inks separated
from its displayed colours. Colour bitmap glyphs follow the same order.

On that file the app's first frame went from 9.2 s to 1.6 s and the sharp frame after a pan
from 3.9 s to 8 ms (M3 Pro, one run each). Fine detail now shows its true average: 1-pixel
black and white lines show mid grey, where separating first showed them 44 levels darker.
2026-11-06 04:02:47 +07:00
Alex c2ef67b505 Effect dialogs: the colour swatch in the progress docs (#5365)
Roadmap, target-app-parity (revision row, status line, evidence bullet and the UI chrome
area row) and ui-parity (revision row and a checklist row) record the swatch beside a hex
colour field, its Color Picker and the test that covers both.
2026-11-06 02:34:17 +07:00
Don Omondi 87a656cdef Shape tools: a shape just drawn shows its bounding box and Live Shape widgets, and its corner and pie widgets drag with the shape tool (#5477) 2026-11-06 01:50:55 +07:00
dotCooCoo 0c272519fb .ai import: symbols and their instances open live
The reader measured a symbol's definition only to find the page art an
instance covered, so every file with a placed symbol instance opened
with that instance as drawn art, or as its page.

- Each `%AI14_BeginSymbol` definition is read by a reader of its own into
  a symbol of the document, with its art (in the symbol's space, y down),
  the registration point its dictionary gives
  (`AI13PatternRegistrationType`, 1 to 9 from the top left) and the
  instances of other symbols in its art. A definition whose art isn't
  read whole isn't kept: type, placed files, non-native art, pattern
  fills, a gradient or an image the reader can't read or place, and
  anything else the reader doesn't read. Its instances come in as the
  page draws them, within the box of the art that is read. A definition
  that holds an instance of a symbol the file doesn't have isn't kept
  either, nor is a definition holding an instance of one of those, and
  their instances aren't read. A definition after the first layer isn't
  read.
- `%AI24_BeginSymbolList` orders the symbols as the Symbols panel lists
  them. A name read twice is numbered ("Dot 2"), and instances name the
  first symbol with the file's name.
- `/SymbolInstance` with `/SymbolRef` and `/RTransformMatrix` comes in as
  a live instance, with the hide and lock set before it and the opacity,
  blending mode, isolation and knockout of the `Xy` after it, as a
  group's. An instance with appearance changes of its own
  (`AI19SymbolAppearanceMap`), of a symbol that isn't read, or placed
  only by `/Matrix` or `/Transform`, comes in as the page draws it.
- Symbol art takes the gradients and art styles read before it, and an
  object in it linked to a listed graphic style keeps the link.
- In a symbol's art, the matrices of instances, gradients and images are
  read in the canvas's space, y up, about the canvas point of the ruler
  origin. Without `%AI3_TemplateBox` an image can't be placed, so the
  symbol isn't kept.
- At most 4,096 definitions are read, and a symbol list names at most
  16,384 symbols.

Tests: reader tests for each case above, and fuzz cases for damaged and
hostile symbol data.
2026-11-06 01:32:42 +07:00
dotCooCoo c4d40de212 Links: the hashing limit is 1 GiB in params text and MCP docs
links.findFiles hashes files up to MAX_HASHED (1 << 30 bytes) and compares larger ones by size only. The params text, the module doc comment and docs/mcp.md said 1 GB; they now say 1 GiB.
2026-11-06 01:32:42 +07:00
Don Omondi 496237242d Type: Show Snap to Glyph Options scrolls the Character panel to the options (#5273)
The Snap to Glyph section is drawn after every character attribute, so
in a short panel it appeared out of sight and the menu item seemed to do
nothing; its checkboxes were also dimmed with no way to turn Snap to
Glyph on from there. Choosing Show Snap to Glyph Options now scrolls the
panel to the section, which starts with a Snap to Glyph checkbox
(View > Snap to Glyph) that undims the guide options.
2026-11-06 01:20:04 +07:00
dotCooCoo ddb64fceb6 .ai import: objects other than type keep some Distort & Transform effects live
An art style with a Distort & Transform effect opened every object but
type as its drawn look. Objects other than type now open as themselves
with these effects live:

- Any object takes a Tweak that moves nothing and Pucker & Bloat at 0,
  beside the effects it already took.
- A path, compound path or group takes Transform without copies, or a
  Free Distort whose corners make a parallelogram, when it is the only
  effect. Neither the style nor anything in the object may paint with a
  gradient or pattern. Everything in the object must be a path, compound
  path or group without a clip, an opacity mask or effects of its own.
  When Transform scales, rotates or reflects, nothing in the object may
  be stroked.
- An unstroked path or compound path whose segments are all straight
  takes Twist when it is the only effect.

Other objects keep the drawn look, as do Zig Zag, Pucker & Bloat of
another amount, a Free Distort with other corners and Transform copies.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-11-06 01:20:04 +07:00
dotCooCoo 7d004de195 .ai import: graphic style libraries open with their styles
A .ai or EPS graphic style library holds graphic styles and their list
in its editing data but no layers, so it came in as its PDF part or
page with the default styles, and Other Library listed those. A file
read as its PDF part or page, because it has no layers or its layers
can't be used, now gets the styles of its list, in list order, as a
file read from its layers does: File > Open shows them in the Graphic
Styles panel and Other Library lists them in a library panel.

Style stroke weights and effects are scaled by the Large Canvas factor
of an EPS, and for a .ai whose editing data is in the units of its
pages by the pages' UserUnit or the declared Large Canvas scale.

Part of #3349
2026-11-06 00:55:18 +07:00
dotCooCoo c63ce28e84 Links: params text and MCP docs say parent folders are resolved
links.findFiles' params text and docs/mcp.md now say that the saved path's
parent-folder steps (.., or the empty components of a classic Mac :: run)
are resolved and that . components in a slash path are skipped before
folder names are compared.
2026-11-06 00:55:18 +07:00