Commit Graph
5119 Commits
Author SHA1 Message Date
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
Don Omondi 446c3f071f SVG import: a clipPath's children clip to their union, each under its own clip-rule (#5840)
A clipPath with several children was imported as one compound path under
the first child's fill rule, so children wound in opposite directions
cancelled where they overlapped, and an even-odd child after a non-zero
one lost its hole. SVG 1.1 §14.3.5 defines the clip as the union of the
children's silhouettes, each computed on its own.

A clipPath with several children now comes in as a group of them as the
clip group's clipping path, each child keeping its own rule; a group
clips to the union of its members. One child imports as before.
2026-11-06 05:32:19 +07:00
bflatastic 887f59c7a3 Tests: the first-frame worker test tolerates another test's placed document
The frame key holds the process-wide placed-document generation, so a test
running beside this one could make a new frame between the two it compares.
2026-11-06 05:32:18 +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 ZidanandClaude Opus 5.5 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
.CooCoo 36c73f7bb9 Type: Enter, arrows, Home, End, Tab and the clipboard apply in order with the frame's text (#2636)
* Type: a Backspace and the text typed after it in one frame apply in order

The Type tool received all of a frame's typed text and IME events
first, and the frame's Backspace, Delete, arrows, Home, End, Tab and
clipboard keys afterwards. Enter went to the tool before the text, and
several Enters in one frame added one line break.

A phonetic keyboard tool can replace a character it typed by sending a
Backspace and the new character: ন for n, then a Backspace and ং when g
follows. When the Backspace and ং arrived in one frame, ং was inserted
and then deleted, so ন was left where ং belonged (#2615).

route_type_input now takes the frame's text, IME events, editing keys,
Enter and clipboard events in one pass, in the order they came. The
Type tool still drops the keys of a frame that began with IME marked
text, and the keys that come while the IME composes. A key or clipboard
event that comes before a new composition in the same frame is now
applied instead of dropped, and an Enter in the same frame as an Escape
no longer types a line break.

* Type: a command bound to Cmd+Enter runs without typing a line break

While the Type tool edits text, Enter went to the tool as a line break
before the shortcuts ran, so a command bound to Cmd/Ctrl+Enter also
typed a line break. Enter now types a line break only when no command
is bound to its chord. Plain Enter, and Cmd+Enter with nothing bound
to it, type a line break as before.

* Type: Enter, arrows, Home, End, Tab and the clipboard go in type_text's ordered pass

Enter, the arrows, Home, End, Tab, Backspace and Delete with modifiers,
Type > Bold and Italic, and the clipboard events now join the ordered
pass in type_text that takes text, IME events and plain Backspace and
Delete, instead of a separate pass that replaced it. route_type_input is
split into type_input, which takes one event, and apply_type_input.
Every event the Type tool takes, text and IME events included, is
removed from the input.

An Enter press whose chord a command is bound to stays in the input for
the shortcuts. The check uses chord_runs_a_command, which runs_a_command
now calls for each press, so the separate Cmd+Enter check in
route_type_input is removed. A plain Enter and a bound Cmd+Enter in one
frame type one line break and run the command.

hook_ime_backspace_and_reinject_builds_vietnamese covers a Backspace and
the text after it in one frame, so the Bangla test is removed. The order
test covers Home, End, Tab, a paste, and a key that comes before a
composition starts in the same frame.

* Type: editing keys after an IME commit apply in the same frame

In a frame that began with a composition, the Type tool dropped every
editing key, also one that came after the IME committed its text, so a
frame of a commit, ← and a letter typed the letter after the committed
text without moving the caret. Editing keys after a commit in that
frame now apply in order. A key that comes while the composition is
still open, and an Enter in a frame that began composing, still belong
to the IME.

* Type: Shift+Enter types a soft return when keys apply in order with the text

The ordered Type tool input passed Enter to the tool without its modifiers, so Shift+Enter typed a line break instead of the soft return main added. Enter now carries the key's modifiers.

* Type: test Home, Tab and Backspace against the frame's earlier text

A new test types text, Home and text in one frame, Tab inside a line,
and a paste followed by Backspace, and checks that each acts on the
text the earlier events left. It fails on main, where those keys apply
after all of the frame's text.

The Enter and Escape loop in shortcuts::handle skips Enter with
`skip` instead of an `if`, which keeps the function under main's
cognitive-complexity limit.
2026-11-05 22:22:47 +01: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 18c0ef06e2 Outer Glow: a colour swatch beside Color opens the Color Picker (#5365)
Effect dialogs that take a hex colour (Outer Glow, Inner Glow, Drop Shadow's fields) show a
swatch beside the colour field: it follows the field as it is typed, and a double-click opens
the Color Picker over the dialog, whose OK fills the field and whose Cancel comes back to the
dialog. While the picker is open it previews nothing itself: the effect dialog previews the
colour when it returns.

The swatch is part of the generic effect parameter grid, so every effect dialog with a colour
parameter gets it, and the Color Picker learns to fill one field of a dialog opened over
(`over_effect`, a parent like Recolor Artwork's).

Test: dialogs::effect::tests::outer_glow_colour_swatch_opens_the_color_picker (swatch drawn
on #ffff00, a typed hex recolours it, a double-click opens the picker on it, OK fills Color and
the effect applies #00ff00).
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
Don Omondi a57328be2f Dialogs: the header that moves a dialog keeps the arrow cursor until dragged (#5246)
Hovering the band that moves a modal dialog (New Document's top edge and
tab row, other dialogs' headings) set the grab cursor, which Windows
shows as the four-way move arrows. It now keeps the arrow, as a title
bar does, and shows the grabbing cursor only while the dialog is being
dragged. Dragging still moves the dialog.
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
FredAppsandClaude Opus 5.5 64f7b49dea Selection: a hover checks the bounds memo once, not once per object
Hovering looks up the bounds of every selected object, and each lookup
went through the memo key check, which compares the whole selection. That
made a hover quadratic in the selection: 1.5 s at 50,000 selected paths.

The walk now checks the key once and shares it across its lookups.

Part of #1280.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:07:34 +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
Don Omondi 22a4dd6285 Libraries: spot colours stay spot colours in every document (#5812)
A library colour kept only its values, so a spot colour came back as a
process colour once its document was closed or in a new one. The library
now keeps a spot colour's ink name and tint (in the .vclibrary file too);
library.use links the paint to the document's spot swatch of that name,
adding it when the document has none of that colour, in one undo step.
A spot swatch dropped on the Libraries panel brings its name and tint.
2026-11-06 00:42:10 +07:00
Don Omondi 8e9625158f Appearance: Delete Selected Item on an Opacity row sets it back to Default (#5742)
Selecting the object's Opacity row, or a fill's or stroke's, and clicking
Delete Selected Item (or pressing Delete/Backspace, or Remove Item in the
panel menu) now sets that opacity back to Default (100%, Normal) in one
undo step. The button used to be dimmed for these rows.
2026-11-06 00:42:05 +07:00
dotCooCoo bed4154597 Links: Find Linked Files resolves parent folders in a saved path
A saved link path such as `Disk:Projects:Art::photo.png` (classic Mac OS)
or `/old/Projects/Art/../photo.png` names a file in `Projects`. Ranking
the files found compared `Art` first, so a file under a folder named
`Art` came before one under `Projects`. The `::` runs and `..` components
are now resolved before the folder names are compared.
2026-11-06 00:24:44 +07:00
dotCooCoo 02f495e647 perf: skip stroke path rebuild for single-stroke samples 2026-11-05 23:55:53 +07:00
dotCooCoo 95df6982ee .ai import: version 7 pattern fills in brush art use their operands
A brush's art is read by a reader of its own, which was given the
patterns defined before it but not the record of which of them use the
version 7 form. A fill in that art with such a pattern then ignored its
offset, scale, rotation, reflection and shear operands, and placed the
tiles by the matrix alone. The brush-art reader now receives that record
as well, so those operands place the tiles. A brush whose art has such a
fill without all nine operands is now not read, as other art the reader
can't read whole isn't; before, the brush was kept.
2026-11-05 23:42:07 +07:00
dotCooCoo 934e6cba53 perf: reuse selected paint bounds for library gradients 2026-11-05 23:42:01 +07:00
Alex 9e2e0e0f69 Pencil: a new stroke with Keep selected off leaves nothing selected (#5752)
Drawing a new Pencil or Paintbrush stroke kept the earlier selection: the engine's
`insert_look` selects the new path only when `keepSelected` is on, and with it off left the
selection as it was. Illustrator starts such a stroke by dropping the selection, and the new
stroke is not selected with Keep selected off, so the command now clears the selection after
inserting a new path.

What stays as before: with Keep selected on a stroke selects only the path it draws (the
earlier selection is replaced); a stroke that continues or redraws a selected path with Keep
selected off deselects that path and keeps the rest of the selection. The Blob Brush keeps its
own Keep selected and Merge only with selection options.

`path.freehand`'s documentation, the engine test
`pencil_with_keep_selected_off_leaves_nothing_selected` and the Tool Options test
`the_new_path_is_selected_only_with_keep_selected_on` cover the three cases. Docs: roadmap,
target-app-parity and ui-parity carry the change.
2026-11-05 23:42:01 +07:00
Harley Hawk a77b9e37d3 Add customizable shape tools and 3D Box add-on
- Extend vectorcraft-geom::shapes with clean-room open/pie/chord/arc ellipse and rectangle builders, per-corner radii, polygon/star corner rounding and deterministic vertex randomization, logarithmic/Archimedean spiral divergence and inner radius, and perspective/isometric 3D box face builders.
- Isolate 3D Box and customizable shape parameters as a VectorCraft add-on under crates/*/src/**/addons/:
  - crates/tools/src/addons/box3d.rs (Box3dTool and CustomShape extras stored under Node::extras["vc-shape-tools"])
  - crates/engine/src/cmd/addons/shapes.rs (addon.shapes.rectangle, addon.shapes.ellipse, addon.shapes.polygon, addon.shapes.star, addon.shapes.spiral, addon.shapes.box3d, and addon.shapes.configure)
  - crates/ui-egui/src/addons/shapes.rs (Object > Plug-ins > 3D Box > 3D Box Tool and context-sensitive Control bar options)
- Keep crates/engine/src/cmd/create.rs, crates/tools/src/shape.rs, and crates/tools/src/draw2/family.rs byte-for-byte identical to main.
- Add translations for "3D Box" and "3D Box Tool" across all 26 .tsv catalogs in crates/ui-egui/src/i18n/.
2026-11-05 23:25:05 +07:00
dotCooCoo 57bf6de206 perf: skip unused SVG path bounds 2026-11-05 23:25:05 +07:00
dotCooCoo 84904020e8 Images: a colorized image's missing-link placeholder shows in its own colors
A linked 1-bit image mask from a .ai file keeps its fill color as a
colorize paint, so the image takes the color once its file is relinked.
Until then the image shows the missing-link placeholder, and the canvas
and PDF export colorized that placeholder too: its light tile turned
nearly transparent and its red cross took the fill color.

ImageBlob::takes_colorize is false for a preview whose pixels are not
grayscale. While it is false, the canvas draws the image and PDF export
writes it as it is, printed separations separate it as an uncolorized
image, and transparency flattening (EPS, PDF 1.3 and PDF/X-1a export,
Flatten Transparency) does not count its paint as transparency. The
paint stays on the image, and a grayscale preview is still colorized.
SVG and EPS export already write only grayscale images colorized.
2026-11-05 23:25:05 +07:00
Locme a3ec061995 .ai import: 6D Art Pen brushes open live, and Windows reads pen tilt and rotation (#372)
Squashed from #5815 onto main.
2026-11-05 23:25:05 +07:00
dotCooCoo f092b0b7a7 .ai import: legacy type keeps its tab stops
A tab definition in type of the legacy format (`numDots numTabs Xb`, a
`leader decimal tabType tabPosition Xe` for each stop, then `XB`) was
skipped, so every paragraph opened with only the default stops every
36 pt. The stops it describes now become the paragraph's tab stops
(left, center, right or decimal, at their positions), and they last
into the next paragraphs and text objects like the other paragraph
attributes. A definition with no stops, or whose descriptions were all
skipped, leaves the default ones. A description with an unknown type or
a position outside 0 to 100000 pt is skipped, and so is one past the
first 1024 of a definition. A text object's `To` ends a definition that
an earlier object left open.

Docs: docs/file-format-parity.md.
2026-11-05 22:26:49 +07:00
dotCooCoo 804d7cee57 fix: preserve queued key order before tool selection 2026-11-05 22:17:33 +07:00
dotCooCoo 869d558d66 fix: follow active flyout tab for thumbnail work 2026-11-05 22:17:33 +07:00
dotCooCoo 456487b873 .ai import: the file opens on its active artboard
VectorCraft now reads `CropAreaActive`, an `/Int` in the dictionary of
the editing data that holds the `ArtboardArray`, as the index of the
active artboard in that array. The reader ignored it before, and every
file opened on its first artboard.

The opened document starts with that artboard active, so the view the
document opens at and the Artboards panel use it. The index counts
every entry of the array, so an artboard that is left out (one with no
area) does not shift it. When the entry is missing, is not an `/Int`,
or names an artboard that does not open, the first artboard is active,
as before. A file that comes in as its PDF part, with its pages placed
where its artboards are, opens on the page of the active artboard, also
when the reading stops before the artboards. The same reader serves the
editing data of EPS and PDF files.

The active artboard is kept on the opened document only; native files
do not save it.
2026-11-05 22:03:55 +07:00
dotCooCoo 48cf28fe48 Links: a classic Mac OS path is named by its file name
A linked image read from a version 7 `.ai` file can keep its path in
the classic Mac OS form, with colons between folders
(`Macintosh HD:Images:photo.tif`). The link's name was the whole path,
so the Links panel showed it as one long name, and Relink to Folder,
the search of the document's folder and Find Linked Files looked for a
file with that whole string as its name.

A path with a colon and no `/` or `\` is now read as a classic Mac OS
path. Its name is the part after the last colon, and `links.info` gives
the rest as `location`. Relink to Folder, the document-folder search and
`links.findFiles` look for that name, and Find Linked Files compares the
path's folders with the folders of the files it finds. The link keeps
the path as stored. Paths with a `/` or `\` keep those as separators,
so a file name may still hold a colon (`/Users/a/x:y.png`).

Docs: docs/mcp.md (Links), docs/file-format-parity.md (version 7
linked images).
2026-11-05 21:46:43 +07:00
dotCooCoo 8caa0e0e59 Layers: closing a document drops its running thumbnail job's image
A thumbnail job that was running when its document closed still sent
its image back, and the image stayed with the worker until a panel next
collected results. ThumbWorker::forget now drops the jobs of the picked
slots, queued or running, and the Layers panel uses it when a document
closes, keeping the same worker. The close test now goes through
File > Close with no panel drawn afterward.
2026-11-05 21:46:43 +07:00
Hamza 728352269d Color Effects: Apply Fill to Stroke and Apply Stroke to Fill become add-on commands
As asked in review: the reference app has no such commands, so they ship
in the Color Effects add-on. paint.fillToStroke / paint.strokeToFill are
now addon.color.fillToStroke / addon.color.strokeToFill in
cmd/addons/color.rs, listed in Object > Plug-ins > Color Effects instead
of the Color panel menu, with catalog rows in all 26 catalogs.

Two fixes from CodeRabbit's review:
- A graph's series marks and labels give the graph the copied paint in
  the same undo step (capture_series_paints / capture_label_styles, as
  paint.setFill does), so drawing the graph again keeps it. Targets go
  through graph::paint_targets, so a series group stands for its marks.
- A copy that would leave the effective paint as it is skips the object,
  so None copied onto a missing fill or stroke adds no item and records
  no undo step.
2026-11-05 21:46:43 +07:00
Hamza 0db0c7ddae Paint: Apply Fill to Stroke and Apply Stroke to Fill (#5530)
paint.fillToStroke and paint.strokeToFill copy each target's own top fill onto its top stroke, or the other way, so a mixed selection keeps each object's colour. They aim and undo like Swap Fill and Stroke: groups and compound paths by their members, each text run with its own paint, and only the characters the Type tool has selected. A stroke given paint at width 0 gets 1 pt, skipNone leaves objects whose source paint is None alone, and the defaults for new art change the same way. Both are in the Color panel menu after Invert and Complement.
2026-11-05 21:46:43 +07:00
bflatastic b700369227 Path menu: the path check stops at the first path it finds 2026-11-05 21:34:32 +07:00
NetterSilverandClaude Haiku 5.5 0001a1c702 Disable Object > Path items when no path is selected
Fixes #5796

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 21:34:32 +07:00
dotCooCoo 52438c7cea Fix item 1747: stop hidden thumbnail panel jobs 2026-11-05 21:18:44 +07:00
dotCooCoo 6e2a32c2a0 .ai import: legacy type used as a mask clips its clipping group
In the legacy format, `Tr` sets the rendering mode of the type that
follows. Section 5.7 of the version 7 specification says that modes 4 to
7 make the text object a mask: 4, 5 and 6 also fill, stroke, or fill and
stroke it, and 7 paints nothing. Sections 15.3.2 and 15.4.5.1 add that
these modes are used in pattern definitions, and the reader does not
read type in a pattern. The reader painted 4 to 6 like 0 to 2 and never
clipped, so the art a text mask clips showed whole.

A text object with characters in modes 4 to 7 is now a clipping path of
its clipping group (`q` ... `Q`, or the layer), as a path painted with
`W` is, and the whole object is the mask. Beside other clipping paths it
nests as they do, and the mode lasts into later text objects. Area type
with linked areas (threaded frames, also when the reader's limits keep
one frame), and type in wraparound text or in a compound path, a group
inside them included, doesn't clip, with a warning.

A `Tr` operand that is not a whole number from 0 to 9 now leaves the
mode as it was; before, 4.5 read as 4 and -4 hid the type.

The new warning and the reader's warning about linked type areas left
out get rows in every catalog.
2026-11-05 21:18:44 +07:00
dotCooCoo e55c6d9d12 .ai import: gradients and patterns are named up to the newline in their names
The Adobe Illustrator File Format Specification 7.0, section 4 (p. 51),
says the name shown for a global object is the part of its name in the
file before the newline
(https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf). The
legacy reader kept the whole name, so swatches and patterns came in
named "Sky\n1". The reader now applies this rule to the files of every
version it reads.

- Gradient swatches, gradient fills, pattern definitions, pattern fills,
  palette cells and art-style pattern fills use the part before the
  newline. Lookups inside the reader still use the whole name.
- When that name is taken, the first free "Sky 2", "Sky 3" is used.
  Taken names are None and Registration, the palette's own swatch and
  group names, the default swatches when no palette replaces them,
  named colors (color operators, gradient stops, version 7 pattern
  strings, the header's and an EPS program's custom colors), the names
  of gradients and patterns that have no newline, and the names given
  to earlier gradients and patterns. Only content the reader reads
  counts: not the alternates of versioned content, the prolog, skipped
  sections or what follows the trailer.
- A name with nothing before the newline comes in as "Gradient" or
  "Pattern". Names without a newline do not change.
- docs/file-format-parity.md lists the rule.
2026-11-05 21:06:27 +07:00
dotCooCoo 2cd68fbdcb Actions: loading an action set checks its size before reading it
Load Actions… read the whole file before checking the 8 MiB limit, so
picking a very large file could take a lot of memory before it was
refused. A new read_head service reads at most a given number of bytes;
the desktop app provides it, and loading an action set reads one byte
past 8 MiB with it. A larger file is refused without reading the rest.
Hosts without the service keep reading the whole file. Automation
roots apply to read_head as they do to read.
2026-11-05 20:55:58 +07:00
Davit Matchakhelidze e271e5eb18 i18n(ka): Georgian for 1 new string
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-11-05 20:47:12 +07:00
dotCooCoo d9332c806b UI-1746: Release closed document layer thumbnails 2026-11-05 20:18:52 +07:00
dotCooCoo 99169d5404 Reject malformed Batch Trace Folder options 2026-11-05 20:18:52 +07:00
FractalEncrypt e0ad7ccf16 Preserve raster coverage of thin curved strokes
Squashed from #3273 onto main.
2026-11-05 20:00:21 +07:00
FractalEncrypt 884a330fae Keep Group Selection drags on the selected outlined component
Squashed from #3272 onto main.
2026-11-05 20:00:12 +07:00
dotCooCoo 7973439cc2 .ai import: a re-encoded font's own encoding pairs decode its type
In the legacy format, `TZ` makes a re-encoded font from an existing one
and can give it encoding pairs of its own, which change only that font's
encoding. Its `useDefault` operand says whether the file's `TE` encoding
also applies to the font (Adobe Illustrator File Format Specification
7.0, section 3.3.1,
https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf). The
reader skipped the body of `%AI3_BeginEncoding` sections and ignored
`TZ`, so every string was decoded with `TE`'s encoding.

Strings in a re-encoded font are now decoded with its own pairs, then
with `TE`'s encoding when `useDefault` is 1, and as Windows-1252 for the
codes left. Strings in other fonts keep `TE`'s encoding. The reader
reads `TZ` in the body of an `%AI3_BeginEncoding` section and wherever
else it appears in the script, with or without the direction operand
and the Multiple Master weight vector. The font names are the last two
names, and only numbers and the weight vector may follow them; the
reader does not use a `TZ` of another shape. The first `TZ` for a font
is used.

`TZ`'s font names also map the re-encoded font to the font it was made
from, as the `%AI3_BeginEncoding` comment does. Type in a re-encoded
font that no such comment names now opens in the font `TZ` names; when
both name the font, the first one read is used.
2026-11-05 19:40:58 +07:00
el-j 02a8928f8e M2.1: copy and cut direct-selected segments and anchors as independent paths (#2122)
Squashed from #3903 onto main.
2026-11-05 19:40:58 +07:00