Saving a legacy .ai of version 7 flattens each transparent image at its own
resolution, limited only by total pixels. The renderer draws at most 32,768
pixels a side, so an image that came out longer than that at its resolution
was cut off and placed as if whole: the part past that length showed the art
under it without the image. The resolution is now lowered until the longest
side fits.
Print as Bitmap limited each page's image to 65,535 pixels a side, but the
renderer draws at most 32,768. A 9000 x 100 pt artboard at a 300 ppi raster
effects resolution printed as an image that stopped at 7,864 pt and was
placed as if whole. The resolution is now lowered until the longest side
fits.
Under --automation-read-root, a control client's ui.menu.list (and
ui.contextMenu.list, which shares the listing) left every File › Open
Recent Files row in, so the client saw the names of recent files it may
not read. file.recentFiles returned their full paths, and
file.openRecentN for such a file answered with a refusal naming the path.
Now the listing leaves out a recent file outside the read roots, as it
already leaves out listed scripts outside them. The other rows keep their
ids, so file.openRecent3 still opens the third file. file.recentFiles
returns only the files inside the roots, in order, and file.openRecentN
for a file outside them ends with "no such recent file", as an empty slot
does. The person's own menus and Home screen are unchanged.
Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
A getter, a conversion, a callback of a built-in function or a file
$.evalFile runs is executed on native frames, so the run cannot stop at a
$.sleep there. Such a call used to only mark the run to pause, so the
code after it ran first and the wait came later. $.sleep with more than 0
milliseconds now throws an Error there, as alert, confirm and prompt do.
toSource checked the 16,777,216-unit length limit only after a whole
string was written. A long string of characters that are escaped could
reach the heap limit first, which ends the run instead of throwing a
RangeError. The limit is now checked before each part is added.
A failure $.evalFile raised for a file it could not run was placed by
matching the uncaught error's message against the last such failure. A
script that caught the failure and threw it again later, or threw a new
error whose message held its text, was reported at the failed file's line
instead of the second throw.
The failure now carries a mark (HostError::Marked) with the exception.
A catch clause drops the mark, a throw raises the value without one, and
a finally block that ends normally throws it on with its mark, nested
finally blocks included. Realm::error_mark gives the mark of the error a
run ended with, and the engine looks up the file, line and column by it.
Pasted art whose SVG filter is renamed to a free name ("Soft 2") had
the new name written into the filter's id. For a filter at the 64 KB
limit the longer id made it too long to draw, so the pasted art lost
the effect. Paste now takes the first free name under which the filter
still fits: a shorter start of its name ("Sof"), else a free letter or
digit. The filter's id is always the effect's name.
Threaded type read from a file was flowed through its frames before
the document's composite fonts were registered, so a story set in one
was measured in the fallback font. Frames then held the wrong share of
the story after opening, until a frame was edited. load_with now lends
the document's composite fonts while it lays out its threads.
Object > Rasterize and the redrawn path of Crop Image allowed images up to
65,535 pixels a side, but the renderer draws at most 32,768. A 100 x 5569 pt
rectangle rasterized at 600 ppi came back 32,768 pixels tall, and the bottom
29% of the art was gone. Such an image is now refused like one over the
64 megapixel limit, and the art stays as it was.
A layer's Lb colour index was its position in LAYER_COLORS, but the file
numbers Magenta, Cyan and Yellow 4, 5 and 6 where LAYER_COLORS has Yellow,
Magenta and Cyan. Save (editing copy and legacy) and open now map the index
both ways; LAYER_COLORS keeps its order and the other 24 presets are unchanged.
A centered label such as Q||1 has no baseline for its empty middle
line, so a label that keeps more leading was not moved back to its
place. The graph now centers a label whose first and last lines hold
characters, whatever the lines between them hold.
While an envelope's contents are edited, the editing data keeps its mesh
in the envelope's dictionary. In some users' files that mesh lies away
from the drawn art by a plain move, the contents sitting with the drawn
art, as if the mesh hadn't followed the envelope when it moved. The
envelope then drew its contents elsewhere, failed the drawn-art check
and kept its drawn art. Nothing in the dictionary records the move, and
no envelope saved outside Edit Contents was found displaced.
A mesh or top object saved in Edit Contents (and a warp of type tried
on its saved grid) whose mesh draws the contents as large as the drawn
art, within half a point over width and height together, but further
from it than that, is now tried again after every other way, with one
more of the import's checks, its mesh moved by as far as the drawn
art's box lies from the box of what it draws. The drawn-art check, the
budget and the page comparison decide as before, so an envelope that
opened live before opens the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tool-edit sweep sent `{"<key>": "on"}` for every documented key, so
`prefs.export` wrote a preferences file named `on` into crates/engine on
every run, and `folder`, `destinationFolder` and `diffPath` strings from
`junk_params` named locations under the working directory.
- `is_path_key` covers `path`, `diffPath`, `folder` and
`destinationFolder`; `safe_value` sends a temp file for a file key and
a temp folder for a folder key, and both sweeps use it.
- A new test builds both sweeps' cases for every command and fails when
a string under one of those keys is outside the temp folder, without
running any command.
effect.deferredStatus only lists the document's deferred stages, so it joins reviewed::ALLOWED. effect.refreshDeferred runs plug-in effects again, so it joins reviewed::DENIED, which fixes every_command_is_reviewed.
The flattener limited an image's resolution only by its total pixels, while
the renderer draws at most 32,768 pixels a side. A long area, such as a narrow
see-through gradient strip down a 2 m banner at 1200 ppi, came back cut off
at that length and was placed as if whole, so the rest of the strip was lost
in Flatten Transparency and in PDF/X-1a, PDF/X-3 and PDF 1.3 exports. The
resolution is now also limited by the longest side, as raster effects are.
A merge left a second copy of the G9 `.ai`/EPS Missing bullet's opening inside the bullet. That copy says
writing the editing data is out of scope, while the same bullet and file-format-parity.md say a
PDF-compatible `.ai` carries an editing copy of the art. The bullet now has one copy, with the newer
brush wording that matches the 2026-11-05 entry above it.