A legacy .ai graph (%_Gs ... %_GS) opened as plain art, with its table,
type, axes and settings left out. A grouped column, stacked column, line
or area graph now keeps them on its group when every record has the
shape and values the version 7 specification's appendix A gives, the
table holds at least one number, the drawn art strokes the plot's left
and bottom edges, and each series group is tagged once as a whole
column, holds only paths and paints them with one fill and one stroke.
The saved art stays as it is on open; Graph Data redraws the graph from
its data afterward, with each series' fill and stroke kept.
Any other graph (pie and scatter graphs, designs, customizations, record
shapes of later versions, tables past 10,000 categories, 256 series or
2,600,000 cells) keeps its art as plain objects with the existing note.
Saving a graph to .ai, in either form, now warns that its data and
settings are not written.
A 3D effect on a fill or stroke worked on copies of the object's fills
and strokes, and the copies were looked up among the object's items by
identity, so a live-effect plug-in before that 3D effect was given
`_context.item` "object" and a deferred stage there was listed with no
item. The drawn art, the 3D meshes and Expand as Wireframes now pass the
original item, so the plug-in is given `{"fill": i}` or `{"stroke": i}`
as docs/plugins.md describes. The paints of a type's first run still have
no item.
Select > Start Global Edit was enabled for any plain group, so a group
holding type, an image or nothing was offered in the Select menu and the
Properties panel's Quick Actions, the Control bar showed its Global Edit
Options, and starting then refused it. The enabled check now applies the
same support check as starting to every selected object, a group's
contents included.
The answer is kept per document revision and selected object, so the
Control bar, which asks every frame, walks a group once per change.
Docs: docs/mcp.md, docs/target-app-parity.md (a new line; the existing
Global Edit line no longer says the menu item checks only for a group).
A document's composite fonts were lent by writing them into the font
database's shared map, and a second lend of another definition of the same
name was skipped while the first was held. A document drawn in a tiled or
floating window while the front document drew, a document placed in one
that defines the name differently, and an export made while another
document is in front could draw their type in the other document's
definition. Registering the front document's fonts during a render could
also change the definition mid-render.
Lending now applies on the lending thread only: each guard pushes the
document's definitions onto a per-thread stack that lookups read before the
database's own fonts, latest loan first, and removes them when it drops,
also when the thread unwinds. The guard is neither Send nor Sync. Lending no
longer changes the database or its generation; while a thread lends
definitions other than the database's, FontDb::generation gives a number of
its own for them, so kept layouts and render caches made in one document's
definitions are not used for another's.
Delete Artboards, Delete Empty Artboards and Cut Artboard removed the
chosen artboards one at a time, walking every ruler guide and the layer
tree once per artboard. They now share one helper that removes the
artboards in one walk and their guides in one more, leaving the same
artboards, guides and selected guides as before.
Empty Artboards in Artwork Cleanup had its own copy of the one-pass
removal. It now calls the same helper, and its test reads that helper's
walk counter.
264 Hindi rows kept English words in a Hindi sentence ("or", "give",
"see"), dropped the word that names what is wrong ("unknown align" read
as "unknown `left`"), reversed a condition ("not editing an opacity
mask" read as "do not edit an opacity mask"), translated enum values the
command reads in English, broke a list of accepted values
("(vertical|horizontal|free)" read as "(vertical Chhorizontal
Chlorfree)"), or repeated a parameter as a "size: आकार" prefix. Each row
now says what its English says, with parameter names, enum values,
placeholders and value lists unchanged, and "positive" reads धनात्मक.
Two tests check every row: no English connecting word outside
backticks, quotes, braces and brackets, and every `a|b|c` list of an
`@msg` row kept as written.
An object in an opacity mask's art is written with its art style, as on
the layers, so a mask whose art has Offset Path opens from the editing
data with the effect live and draws the masked object as it did. This
covers a hidden art root and masks inside another mask's art.
Geometry effects on mask art that no art style holds, such as Roughen,
are applied to the art written, in the masks of layers, symbols and
pattern tiles. Before, the mask's art was written without them.
The test's second and third wheel turns step to whichever families the
process has loaded at that moment. When another test has loaded a test
font without Latin glyphs, such as Alias Jointest Sans from the Glyphs
panel tests, the turn applies it, missing glyph protection keeps "Hi" in
Source Serif 4, and the assert_ne at the second turn fails. The test now
turns the preference off, as the font menu tests in tests/font_menu.rs
do, so each turn changes the text's font whatever family it reaches.
With downsampling on, each piece of an image bent by a Warp effect was
resampled for its own placed size, so a warp wrote one image XObject for
each size a piece was placed at (four for a 4 x 4 warp that stretches
across, eight for a colorized one with its soft masks). The pieces are
now resampled for the widest and the highest size any piece places the
image at, and all paint that one image. Pieces off the page count too,
and another placement of the same image keeps its own size.
Without downsampling, a colorized image and a CMYK image with a grid
mask no longer key their cache by placed size, so each is decoded and
encoded once for all the sizes it is placed at, as plain images are.
When a recovery copy could not be written, the status bar said only
"Couldn't save recovery data: <error>", and a copy that failed on the
background worker showed as "Saving recovery data for <title> failed:
<error>". Neither said which folder was in use or how to pick another.
The message keeps the error text (and the document's title when one
copy fails), then names the folder copies go to, says whether it is
the default recovery folder or the folder set in Preferences, and
names the setting that picks another folder: Preferences → File
Handling → Data Recovery → Folder (recoveryFolder). The recovery code
writes the same line to the log. The folder is the one in use when the
round started, so a preference changed while a copy runs does not
change the folder the message names.
A copy that fails now reports its own message, so the job's result is
Ok rather than an error, and the job no longer adds "<label> failed:
<error>" over it. A store the host installed (browser storage) has no
folder to name: its failed copy shows "Saving recovery data for
<title> failed: <error>", as a copy on the worker did before, also
when the copy was written without the worker.
- engine: recovery::folder returns the folder in use and whether the
recoveryFolder preference names it; recovery::store uses it.
- Four @msg rows in each complete-message catalog, and in the bg, bs,
da and id catalogs, which translate the old message.
- Control replies to a client confined to read roots show a recovery
folder outside them, and the paths under it, by the folder's name, as
they already show other paths outside the roots. That covers the
folder in use and the last eight folders a failure message named.
- docs/target-app-parity.md: a line next to the Data Recovery Folder
preference.
Object › Plug-ins › Cutting Tools › Banner Hems and Grommets…
(`addon.cutting.banner`) marks where a printed banner is hemmed and where
its grommets go. The banner is the bounds of the selected art, or each
artboard when nothing is selected. On each chosen side the command adds a
hem line a set depth inside the edge (1 in by default) and grommet marks a
set inset inside it (0.5 in): one at each end of the side and the rest
spread evenly, as few as keep them at most a set spacing apart (24 in). A
corner that two chosen sides share gets one grommet. Each grommet mark is a
circle (27 pt by default) with a cross through its center. The marks are
stroked in a chosen swatch ([Registration] by default) with no fill, in one
group named Banner Marks per banner on top of the current layer, selected,
as one undo step. While a clip group is edited in isolation, the group goes
outside the outermost clip group, as Weed Box and Lines does.
The command refuses every side off, no hem and no grommets, and bad params
with a message. It also refuses a hem or inset that reaches the far edge of
the banner when one of two opposite sides is chosen, or that meets the marks
of the opposite side when both are chosen, an inset of more than half a
chosen side's length, and more than 100000 grommets. The dialog has Preview.
The grommets' distance from the edge is `grommetInset`, so the dialog shows
it as Grommet Inset with its own catalog rows. The new Diameter catalog
rows also translate the Diameter field of the `shape.flare` command dialog,
which had no rows.
Docs: docs/mcp.md (Cutting Tools add-on), docs/target-app-parity.md and
ROADMAP.