Commit Graph
2434 Commits
Author SHA1 Message Date
BFlat c0887164cc Merge pull request #6258 from dotCooCoo/fix/plugin-clear-cache-test
Plug-ins: clearing every plug-in's cache is tested in a binary of its own
2026-11-09 07:46:33 +08:00
dotCooCoo 987f50619a Merge main into feat/trace-keep-colors 2026-11-08 13:49:11 -08:00
dotCooCoo b817baca58 Merge main into feat/trace-keep-colors 2026-11-08 13:45:04 -08:00
dotCooCoo 83248b9bb4 SVG Filters: export writes a filter live only when import can read it back
SVG export wrote an SVG filter as a live <filter> when its SVG 1.1 form
was at most 64 KB, without counting the region and id it writes into
it. A filter close to the limit came back over it, SVG import dropped
it, and the reopened object had no filter.

Export now leaves room for the id and the measured region, checks the
SVG 1.1 form the way import does, and counts each character past U+00FF
as the character reference Latin-1 output writes. A filter that does not
fit is exported as the other pixel effects are. The SVG 1.1 form and its
check are kept for the filters seen last, as checked filters already
are, so export does not check one filter again for each object.
2026-11-09 04:19:44 +07:00
dotCooCoo 1e21154e4d Symbols: symbol.instance.setAppearance refuses a fill or stroke an object does not draw
Squashed from #6232 onto main.
2026-11-09 04:19:25 +07:00
dotCooCoo 4b665ad13c Plug-ins: clearing every cache is tested in a binary of its own
`plugin.clearCache` without an id empties the cache of every plug-in
installed in the process. In the engine's library tests it could empty
the cache of another storage test running at the same time, and its
`bytesFreed` could count that test's cache. It now runs in
`crates/engine/tests/plugin_clear_cache.rs`, and the library test checks
that clearing one plug-in's cache leaves the other plug-in's cache.

The storage fuzz test starts each case with an empty plug-in store,
deletes the store when the case ends, and installs `fuzz.storage` with
`replace: true`, so the cases that shrink a failure install their
module.

docs/mcp.md says that files `plugin.remove` can't delete stay in the
folder renamed with `.removed` until the plug-in is removed again.
2026-11-08 13:03:31 -08:00
onion3130 eaab4fd516 Trim to hermetic font test only (Source Sans 3, not Inter) 2026-11-09 04:02:29 +07:00
dotCooCoo 7b8d5ba8f5 Type: Tab and Shift+Tab move a list item down and up a level
Squashed from #5037 onto main.
2026-11-09 03:39:05 +07:00
dotCooCoo 0de0cb7cb9 Image Trace: Ignore Color can leave out only the background
Squashed from #3952 onto main.
2026-11-09 03:38:46 +07:00
dotCooCoo 8138dc69ea Merge main into feat/trace-keep-colors 2026-11-08 12:24:14 -08:00
dotCooCoo 11f1d228de Type: Align to Path moves each character by its own font's metrics
Align to Path moved the whole line by the largest ascent and descent
among its characters. Each character now moves by its own font, size
and vertical scale. Ascender runs the top of the font's em box along
the path: the ideographic em box that the font's BASE table gives, or
in a Chinese, Japanese or Korean font the one that its typographic or
hhea box, a one-em box-drawing cross or its ideographs give; else an
em box centered on the cap height. Descender runs the bottom of the font's
bounding box along the path, and Center runs a line at half the height
of the top of the font's H along it. In vertical type, Ascender and
Descender move the characters half an em, and Center leaves them on
the path.

Existing documents with Align to Path set to Ascender, Descender or
Center lay their type out at a new distance from the path.
2026-11-09 03:14:41 +07:00
dotCooCoo 25ecee0c40 Color: Generic CMYK tints gain as CGATS TR 001 gives for SWOP printing
Generic CMYK's tone curves now pass through the CGATS TR 001 tone value
increase of each ink at 40% and 50% (C 20.0, M 17.6, Y 16.8, K 21.6 at
50%), and its 16 Neugebauer primaries mix linearly, so a single ink's
tints gain as TR 001 gives. The 300% ink limit stays. The primaries, the
black-generation curve and the black point are VectorCraft's own values.

- The black point is 90% black under neutral CMY that fills the ink
  limit: sRGB black separates to C75 M68 Y67 K90 instead of C67 M67 Y67
  K100.
- Black follows a curve over the gray component of the black-free
  solve, raised by how far that solve stays lighter than the target.
  Grays from sRGB 177 up carry no black, and sRGB 128 gray separates to
  C50.5 M42.2 Y42.0 K7.5 instead of C38.8 M26.3 Y29.9 K24.4.
- Display of CMYK fills, swatches and imported CMYK values and the
  separation of RGB and Lab colors use the same model.

Paper white still separates to C0 M0 Y0 K0, but other separations
change, and so does the display of CMYK colors with ink. Tests that held
numbers of the old model are updated.
2026-11-09 03:14:41 +07:00
dotCooCoo c94269ff72 Type: Composite Fonts… sets each class of character in a font of its own
Squashed from #6244 onto main.
2026-11-09 03:08:23 +07:00
dotCooCoo b9d999fe26 Asset Export: the 3D mesh triangle limit bounds memory across objects
The 2,000,000-triangle limit on a 3D object's meshes is now checked as
each mesh is built, so an object past it is refused before the rest of
its meshes are built:

- Revolve counts each fill's and stroke's mesh as it builds it, instead
  of building every one first.
- Inflate still solves every shape's membrane at once (the eye depends
  on the highest puff), but builds each solid only when its mesh is
  reached (inflate_meshes), so a refused object leaves the later
  solids unbuilt.
- All the objects of one 3D asset share one budget
  (three_d_meshes_of), so assets.export3D refuses an asset whose
  objects pass the limit together.
2026-11-09 03:08:23 +07:00
bflatastic faec1f4290 Links: the moved-document tests compare the found path as a path, so Windows separators match 2026-11-09 03:08:23 +07:00
dotCooCoo faa7b8ef6a Links: a missing linked file keeps its relative folder when a moved document is saved
Squashed from #6231 onto main.
2026-11-09 03:08:23 +07:00
bflatastic d4f2cf5394 Plug-ins: the fuzz tests install each case's module in place of the one before 2026-11-09 03:08:22 +07:00
dotCooCoo 218c711181 Plug-ins: a plug-in keeps settings and a cache of its own
Squashed from #6228 onto main.
2026-11-09 02:54:33 +07:00
dotCooCoo 1f38a16329 Commands: an ids list with an entry that is not an id is refused instead of dropping the entry
Squashed from #6179 onto main.
2026-11-09 02:54:32 +07:00
bflatastic 02d877d043 .ai import: the unread-PDF-part test expects the cut-short note for its large image 2026-11-09 02:54:32 +07:00
dotCooCoo b5adb679e4 .ai import: images past 2^25 pixels in the editing data keep the layers
Squashed from #5143 onto main.
2026-11-09 02:43:13 +07:00
Don Omondi 3bb43b2fe6 EPS import: composite fonts draw their Type 42 descendants' glyphs
A composite (Type 0) font with the 8/8 mapping picks a descendant font
and a code in it with each pair of bytes (PLRM 3rd ed., 5.10). The reader
set its strings as type in a font of the composite font's name, the
bytes read as Latin-1 and at a thousand times the size, so EPS from
pdftops of a PDF with embedded TrueType fonts showed nothing, or one
black shape, where its type was.

Characters now go to their descendants: a Type 42 font's glyphs are drawn
from the TrueType outlines its sfnts strings carry (Adobe Technical Note
5012), by the glyph index CharStrings gives their names, spaced by their
own widths or the show operator's numbers; charpath and stringwidth read
them too. A Type 3 descendant runs its procedures, and a descendant found
by name sets its character as type at the composite font's size.
2026-11-09 02:24:54 +07:00
NetterSilverandClaude Haiku 5.5 b142814bbd Reject a plus sign in type.insert unicode hex digits
Found while testing crates/engine/src/cmd/typemenu.rs:614 (unicode_char)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 02:24:50 +07:00
NetterSilverandClaude Haiku 5.5 b1fd06090c Don't treat a pattern named "Name +N" as a numbered copy on import
Found while testing crates/engine/src/cmd/patternlib.rs:99 (existing_pattern)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 02:03:09 +07:00
SBaksa d329dd05e1 Export for Screens: a leading dot in an artboard name is a dash
A name like .draft would otherwise write a hidden file on Unix-like systems.
2026-11-09 02:03:08 +07:00
SBaksa 0b3ea7c1d2 Export for Screens: an artboard name keeps its dots (#6143)
The artboard and asset file stems came from unique_file_names, which turned
every character except letters, digits, - and _ into a dash, so the dot of
example.artboard became a dash. Export for Screens now uses a variant that keeps
dots (a name of only dots takes the Artboard-N fallback); safe_file_name still
drops trailing periods and guards device names.
Test: an_artboard_name_keeps_its_dots.
2026-11-09 02:03:08 +07:00
Sven Lafebre afe8294c7d Test: anchors picked with a Cmd-lent Direct Selection over other tools are what Copy takes (#6123)
With the Rectangle and Ellipse tools, Cmd lends the selection tool chosen
last, and Copy already took the anchors Direct Selection picked. The
Curvature tool, which Cmd lends Direct Selection, copied the whole path
before this PR, as the Pen did.
2026-11-09 02:03:05 +07:00
Sven Lafebre 748ac5cf6e Pen: keep a lent pick across tool resets; a gesture that picks nothing keeps the Pen's anchor (#6123)
The remembered selection is now tied to the document it was made in, by its
uid, instead of being cleared on every tool reset: another view of the same
document, or closing an inactive tab, kept the anchors picked but made Copy
take the whole path.

A lent gesture that leaves the selection as it found it (a Cmd+Shift marquee
over nothing) is no longer recorded as a pick, so the Pen's last anchor stays
its own and Copy takes the whole path.

Also records #6123 in target-app-parity.md.
2026-11-09 02:03:05 +07:00
Sven Lafebre 91a0ba4231 Pen: Copy and Cut take the anchors a Cmd-lent Direct Selection picked (#6123)
With the Pen or Curvature tool, Copy and Cut ignore the anchor selection,
so that the last anchor the Pen leaves selected doesn't make Copy take just
that anchor (#2122). Anchors picked with the Direct Selection that Cmd/Ctrl
lends those tools were ignored too, and the whole path was copied.

The session now remembers the selection the lent tool leaves when it gives
the tool back. While that is still the selection, Copy and Cut take the
picked anchors and segments, as with Direct Selection chosen.
2026-11-09 02:03:05 +07:00
ruchirguitar b1f52d1369 Select Text by Font: limit it to artboards or layers (#5529)
Squashed from #5578 onto main.
2026-11-09 02:03:01 +07:00
VladimirCeric e3817d6b50 Typographer's Quotes: a lone typed ” opens like a straight quote
Squashed from #5459 onto main.
2026-11-09 02:03:00 +07:00
dotCooCoo 0f926b571f Merge main into feat/trace-keep-colors 2026-11-08 10:58:31 -08:00
dotCooCoo fb848d28ac .ai import: objects in pattern and brush art take the file's art styles
A pattern's definition (in either form) and a brush's art were read
without the file's art styles, so an object there with a style came in
as the look it draws, with a "drawn look" note, while the same object on
a layer or in a symbol came in as itself with its effects live. The
three definition readers now take the art styles as the symbol reader
does, and an object in a pattern's art with a style in the file's style
list keeps that graphic style's id.

An art styles section inside a symbol's, a pattern's or a brush's art
is now skipped, so it no longer replaces the file's own art styles.
2026-11-09 00:36:09 +07:00
dotCooCoo 50bf521d45 Swatches: a new swatch holds no link or placement from the paint it is made from
swatch.new stored a gradient or pattern as it was given. A gradient
swatch made from a gradient swatch by name, from a fill linked to one,
or from a gradient that names one (the Gradient panel's add to
swatches) kept the link to that swatch, so copying art that uses the
new swatch brought the other swatch along too. A pattern swatch made
from a current fill picked up from art with transformed tiles kept
that transform, and art the swatch was applied to took it in place of
its own.

The new swatch now drops the gradient's link and stores the pattern
untransformed. A gradient swatch keeps its aspect as before.
2026-11-09 00:35:30 +07:00
dotCooCoo a37ad90e6c .ai import: type keeps Standard Vertical Roman Alignment from its editing data
The modern type reader read key 35 of a character style only for
Tate-chu-yoko (3). A run whose key 35 is 1 (Standard), in the run, its
character style or the document's defaults, now opens with Standard
Vertical Roman Alignment on, so its half-width characters stand upright
in vertical type. Horizontal type keeps the mark with no change to its
layout. A value of 2 (rotated), 3, any other value and no key at all
leave it off, VectorCraft's default.

- ate.rs: Style reads the flag and passes it to CharStyle, with a unit
  test over defaults, character styles, run overrides and bad values.
- tests_native.rs: a vertical and a horizontal story with Standard,
  rotated and unset runs; the test of values the reader ignores also
  checks the flag.
- import_fuzz.rs: the vertical fixture also checks the flag.
- docs/file-format-parity.md: one line on the mapping.
2026-11-09 00:35:02 +07:00
dotCooCoo cca9a42c6c Effects: Effect › SVG Filters applies the document's SVG filters as live effects
Squashed from #6200 onto main.
2026-11-09 00:34:23 +07:00
dotCooCoo 732434b4e5 Effects: placed gradients move with Transform and Free Distort effects
A gradient placed with the Gradient tool stayed where it was when the
object's Transform or Free Distort effect moved, turned or scaled the
outline, so the shape slid across its own gradient.

The Transform effect now maps placed gradients by its own transform,
and each of its copies takes them as many times as it takes the
transform. Under Free Distort, each path's placed gradients move and
scale from the path's bounds before the effect to its bounds after it.
This change does not alter gradients fitted to the object.

The canvas, Expand Appearance and export, Pathfinder and compound
shapes, groups, a fill's or stroke's own effects, and Object › Expand
of a gradient fill apply the same rule. In a group, the own fills and
strokes of the group, of the groups inside it and of a symbol's art
follow it too, and images and symbol instances that a group's
Transform effect moves take the placed gradients of their own fills
and strokes along. A group whose path keeps its shape because the
effect would pass the geometry budget keeps its own gradients in
place.

This changes how existing documents draw: a gradient placed on a path,
or on an image's or symbol instance's own fill, inside a group with a
Transform effect now moves with it. Before, only the gradients of type
moved (#2214).

How gradients follow each effect is VectorCraft's own rule. The
Distort & Transform help page describes what the effects do to the
object:
https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/distort-and-transform-effects-in-illustrator.html
2026-11-09 00:34:12 +07:00
dotCooCoo 332e115b68 Plug-ins: the bulk memory fuzz generators follow the paint generators
Moves the bulk memory case types, strategies and their test after arb_paint, so they no longer share the lines after output where other fuzz generators are added.
2026-11-09 00:31:14 +07:00
dotCooCoo 44432a4310 Plug-ins: the fuzz test generates bulk memory operations
plugin_fuzz now builds plug-ins whose run executes memory.fill,
memory.copy (overlapping ranges included), memory.init followed by
data.drop, and data.drop followed by memory.init. Lengths and offsets
are sampled at zero, small, page and mebibyte sizes, at an exact end,
one before and one past it, with copy sources and destinations and init
memory and segment offsets chosen separately. Fill and copy can repeat
up to about one fuel slice of copying.

Each case runs directly under the default budget, a small fixed budget
or one near its estimated cost. A run must trap exactly when a range
leaves memory or the data segment (empty once dropped), and must finish
when it is in bounds and has ample fuel; otherwise it may hit the
instruction budget. The same plug-in then runs through plugin.run: a
failed run leaves the document unchanged and the document still renders
and exports.

bulk_memory_cases_reach_their_instruction checks that each operation's
module contains its instruction and traps on a small out-of-bounds case.
2026-11-09 00:31:14 +07:00
dotCooCoo 63dfcba8ba Envelopes: a top object's sides share the content by control polygon length
A top-object envelope splits its outline into four sides at four corners
and maps the content onto the Coons patch of those sides. The sides
shared out the content by length, and the corners were the anchors or
curve points nearest the bounding box corners.

Each piece of a side between anchors and corners now takes a share of the
content's width or height in proportion to the length of its control
polygon, spread along the piece by its curve parameter. A straight
segment spreads it evenly by length. A rounded rectangle stays close to
the identity, and the content follows the curves of a side whose segments
differ in size.

Each corner is now the nearest anchor to its bounding box corner among
the sharp anchors that point out of the outline and lie within 47.5% of
the box's width and height from that corner. A handle shorter than
0.01 pt, or tangent unit vectors that differ by 0.01 or more in x or y,
makes an anchor sharp, and the first one along the outline wins a tie.
Without such an anchor, the corner is the outline's nearest point to the
box corner, moved onto the end anchor of its segment within 0.5% of the
segment's parameter. Envelopes opened from .ai files keep the corners the
file saves.

These are VectorCraft's own rules. Existing top-object envelopes redraw by
them. Gradient meshes made from a path keep their sides measured by length.

- crates/doc/src/live/mesh.rs: Coons::from_top_object and the corner
  search, used for top-object envelopes.
- EnvelopeKind::TopObject gains an optional `corners` field (anchor
  indices); the .ai importer fills it from the mesh's corners.
- Params text of object.envelope.makeWithTopObject, docs/mcp.md
  (Envelopes) and a progress line in docs/target-app-parity.md.
2026-11-09 00:31:06 +07:00
dotCooCoo b5e6f6e80f Plug-ins: Safe Mode starts without plug-ins, offered after a launch stops while one loads
Squashed from #6185 onto main.
2026-11-09 00:30:24 +07:00
dotCooCoo 581ced3213 Transform: Align to Pixel Grid keeps an object's horizontal and vertical edges on the pixel grid
Squashed from #6169 onto main.
2026-11-09 00:29:28 +07:00
dotCooCoo b568977af4 Asset Export: 3D objects export as USDA, USDZ, glTF or OBJ meshes
Squashed from #6168 onto main.
2026-11-09 00:29:05 +07:00
dotCooCoo 657a99b629 Actions: Load Actions… reads action sets saved as .aia files
Squashed from #6149 onto main.
2026-11-09 00:28:25 +07:00
dotCooCoo 1d007bc3c8 Artboard tool: buttons on the selected artboard's edges add an artboard beside it
Squashed from #6111 onto main.
2026-11-09 00:27:52 +07:00
dotCooCoo fb413b39f3 Title bar: a Linux and BSD preference uses the app bar where the desktop draws the frame
Squashed from #6101 onto main.
2026-11-09 00:27:21 +07:00
dotCooCoo c0a8ebb70f Graphs: graphic styles and Default and Swap Fill and Stroke update a series' stored paint
Squashed from #6094 onto main.
2026-11-09 00:26:44 +07:00
dotCooCoo a409f34ebe .ai import: placed PDF and .ai pages open as placed documents
Squashed from #6083 onto main.
2026-11-09 00:26:34 +07:00
dotCooCoo 434c032f9e Symbols: Direct Selection sets the solid fill and stroke of one object in a dynamic instance
Squashed from #5962 onto main.
2026-11-09 00:26:11 +07:00
dotCooCoo cd8e98c6cd DXF: a CNC / CAM preset sets the export options for cutting software
Squashed from #5500 onto main.
2026-11-09 00:25:53 +07:00