Changing Preferences › Hyphenation › Exceptions bumped each open
document's revision, but the caches of laid-out type outside the text
crate were keyed on the font generation alone. The renderer kept the
glyph geometry, raster-effect pixels, live-effect and envelope art,
pattern tiles and placed symbol art of unchanged type objects, so
hyphenated area type kept its old line breaks on the canvas. The
hit-test art of blends and envelopes, the Fit to Artwork Bounds answer,
the layout caches of the Type and Touch Type tools and the out port's
overflow mark were keyed the same way.
vectorcraft_text::layout_inputs() returns a number that changes with
the fonts and with the hyphenation exceptions. Every one of those
caches now keys on it instead of FontDb::generation(), and envelope ink
uses it in place of the private envelope_ink_inputs(). The hit-test key
hashes it together with the plug-in revision.
Tests: a renderer that drew hyphenated area type, alone, with a drop
shadow, in a group with a Transform effect, in a symbol instance and in
an envelope, draws it as a new renderer does after the exceptions
change; the out port of selected area type shows the overflow that the
new exceptions cause. The render test helpers that run a step again
when the fonts change during it now also run it again when the
exceptions change.
Session::replace_document on a tab other than the current one cleared
the bounds of type set in a font named like a composite font and left
that type with estimated bounds until the document came to the front.
- The replacement's type without bounds is measured at once, in the
document's own composite fonts, lent for the measuring. The front
document stays the one whose composite fonts are registered.
- When the document then comes to the front unchanged, its type in its
own composite fonts is not measured again. Type set in a composite
font only another document defines is still measured again, as
missing, and so is type set in a composite font name that another
thread's loan held with a different definition.
Type with an inline graphic was resolved only in the layers, so type in
the art of another symbol, a graph design, a pattern tile or an opacity
mask kept the size its graphic had when it was made. The symbol holding
that type, its instances and the inline graphics that show it were
measured from the stale layout.
- Inline graphics are resolved in every tree that holds type. Symbols
are resolved in dependency order, so a symbol's type is laid out
before the type that shows that symbol inline. Edits, opening,
reverting and Refresh Font List use the same pass.
- The pass visits each object once: it resolves the children first and
copies a node only when something below it changed. It measures each
symbol's art once per pass.
- An inline graphic of a symbol whose art shows that symbol inline,
directly or through other symbols, is an empty slot. The symbols on
such a cycle are found once and cached until the symbols change.
- A changed inline graphic also clears the text's cached baselines.
A file read for Place, for the place cursor or dropped on the canvas,
a bitmap read for Paste, and a paste waiting at its Swatch Conflict
dialog are no longer placed or pasted in a document that File › Revert
replaced while they waited. Revert keeps the document's tab and uid, so
these were placed in the reverted content with the selection, clipboard
and insertion context of the content it discarded.
- DocState::reverts counts the Reverts of a document; replace_document
increments it, and a batch that fails puts it back with the document.
- Reads, deferred swatch pastes and web canvas drops keep the count they
started with and are dropped, with a status message, when it changed.
- A paste given after a Revert starts a chain of its own instead of
following a paste given before it.
file.close {index} on a tab other than the current one made the
document now at the closed index current and reset the tool, so the
document the user was working in went to the back. The current
document now stays current, found by its id as document.move does, and
its tool keeps its state. Closing the current tab still makes its
neighbor current.
Docs: docs/mcp.md, docs/target-app-parity.md.
The profiles Edit > Assign Profile gives a document stayed the active
color settings after another document came to the front, so a new,
opened or switched-to document drew and converted its colors through the
previous document's profile, and closing the last document left it in
use.
The session now puts the front document's profiles in use (the working
spaces where it has none, or no document is open) after a new or opened
document, a tab switch, a close, File > Revert, every command including
each batch step and Undo or Redo of Assign Profile, a failed batch or
plug-in call, a script's rollback and slice boundaries, a canceled
preview, undo group or undo scope, and for each document Export All Open
and the update of placed documents work in. The active settings are
rebuilt, and the documents redrawn, only when they change.
The eye and lock on a guide's row in the Layers panel hide or lock that
guide alone, in one undo step, and a drag down either column takes guide
rows along with the other rows it passes. A hidden guide is not drawn and
does not snap, and its row is dimmed; a locked guide is drawn and snaps
but can't be selected, moved or deleted. Both can be changed while the
guide's layer is hidden or locked, and the guide shows or can be edited
only when its layer allows it as well. The state is kept through save
and reopen.
- layer.setProps takes ruler guide node ids; every other command still
refuses them.
- guide.list rows carry the guide's node id and its own hidden and
locked state.
- Alt-click and Ctrl/Cmd+Alt-click on a guide row's eye or lock act on
the other layers, as on any row in that layer.
- Object > Lock > Selection and Object > Hide > Selection lock or hide the
selected ruler guides with the selected art, in one undo step, outside
isolation mode. Unlock All and Show All, document scope, also unlock or
show guides locked or hidden on their own and select them as guides.
- Guide rows dropped on the Layers panel's trash delete their guides
(guide.remove {indexes}). A click on a guide row clears the highlighted
rows, so Delete deletes the guide; with no row highlighted the trash
deletes the selected guides.
- guide.remove and guide.setLayer refuse index together with indexes and
an indexes value that is not a list, and count a repeated index once.
guide.select, guide.remove and guide.setLayer refuse an indexes list of
more than 10,000 entries; guide.setLayer cut such a list short before.
- Moving, deleting or moving to another layer a guide that is hidden or
locked on its own fails with "the guide is hidden or locked"; a guide
on a hidden or locked layer still fails with "the guide is on a hidden
or locked layer".
Docs: docs/mcp.md, docs/target-app-parity.md, docs/ui-parity.md.
With Real-time Drawing off, a command that ends a drag first (Undo, Redo,
Clear History, a plug-in command and the others) applies the drag's
deferred preview, but dropped the warning that preview gave, which
releasing the drag shows in the status bar.
- The applied preview's warning is now kept while a top-level command or
the tool's actions run. A command's result carries it as
`previewWarning`, and as `warning` when the command has no warning of
its own, so the status bar shows it.
- Typing kept as its own step when a tool's drag begins, or when Cmd
lends the Selection tool, shows the warning in the status bar.
- The Cmd-lend path no longer ignores an error from keeping the typing.
Docs: docs/mcp.md (undo/redo row), docs/target-app-parity.md.
Cancel on a pattern that Make created for the edit now removes every
swatch of it, a color group's included, and turns every paint of it
left in the document into no paint: art outside the editing layer,
opacity mask art, type runs, symbol definitions, graph designs, other
patterns' tiles (a copy Save a Copy made included), graphic and
character styles, brush art, and the fill, stroke and appearance new
art takes. Delete Pattern clears its paints in the same places, and a
Pattern Options rename renames them there; both reached only the
layers before. Delete's `unpainted` counts the document paints it
cleared.
Cancel after a Pattern Options rename during the edit gives the
pattern its old name back with its swatches and paints, unless another
pattern, swatch or color group has taken that name; before, the
definition went back to the old name while its swatches and paints
kept the new one.
Undo and Redo restore new art's paints with the document, including
the appearance inherited from the selection with New Art Has Basic
Appearance off. A rename preview that is replaced or canceled starts
again from the paints the interaction began with, and a canceled
preview takes the selection's appearance again.
The new General preference Show Bounding Box While Moving and Drawing
(`boundingBoxWhileMovingAndDrawing`, off by default) keeps the bounding
box and its handles visible during two kinds of drag, with View › Show
Bounding Box on. While a drag moves the selection (a Selection tool
move, an Alt-drag copy, or a shape tool's move by the center point),
the box is drawn where the drag has moved the selection. The box is
measured once when the drag starts and offset by each step's move.
While the Rectangle, Rounded Rectangle, Ellipse, Polygon and Star tools
draw a shape, the box is drawn around the shape. A rotation and a
widget drag still hide the box until the release. The Line Segment
tool, the Pen, the Pencil and the Paintbrush draw no box.
With the preference off, the box hides during a move (#737, for #712)
and while a shape is drawn (#5839), as before. A Selection tool move
with Real-time Drawing off still moves the box with the pointer, and a
shape tool's move by the center point hides it in both modes.
With Real-time Drawing off, the box of a shape being drawn is measured
from the pending preview, in the color of the layer the shape goes on.
A shape being drawn shows no box until a preview step has succeeded,
so a failed preview does not show the box of the selection from before
the drag. The preference's label has a row in every catalog.
Session::deferred_move() is replaced by Session::move_drag_box(), which
returns the moved box, and Session::deferred_transform_box() now also
returns the layer color of the box.
Docs: docs/mcp.md, docs/target-app-parity.md.
A library loaded from outside a confined client's read roots keeps its
`loaded/<file name>` id for the session, in ui.inspect, ui.docking and
the library commands, so an id taken from an earlier reply still finds
the same library after another library with the same file name is
loaded. swatch.library.copyToUser leaves out `path` when the file is
outside the read roots, as the saves do.
The ids come from the engine's concealed_library_ids, which now reads a
path written with either separator, counts another platform's absolute
path as outside the roots, and keeps each id in the session.
Each built-in category (Mobile, Web, Print, Film & Video, Art &
Illustration, Branding, Social) has a New Document profile, a native
document in the New Document Profiles folder next to the preferences.
file.new copies the profile's swatches, graphic styles, brushes, symbols,
patterns, character and paragraph styles, rulers, Document Setup options,
assigned color profiles and print settings into the new document. The
preset's size, units, artboards, bleed, background, color mode and
resolution still apply, and the profile's art and layers are not copied.
- file.newProfiles lists the profiles, file.newProfiles.edit opens one
as a document (writing the shipped profile first), and
file.newProfiles.reset deletes its file. file.new takes `profile`.
- Saved and Recent presets keep the profile of the preset they came from.
- A profile that cannot be read, is larger than 64 MiB or is not a native
document leaves the defaults, and file.new reports it in profileError.
- New Document has a Manage Profiles pop-up with Edit and Reset.
Docs: docs/mcp.md, docs/target-app-parity.md.
Object › Plug-ins › Image Tools gains Color Mask… (`addon.image.colorMask`),
Remove Color Mask and Apply Color Mask. Color Mask hides colors of the
selected images without changing their pixels. It matches colors as Remove
Background does: the edge color, the corner color or picked colors, with
Contiguous, Match, Tolerance and Softness. The mask is kept with the image
object and saved with the document: its settings, and a PNG of the image's
pixels with the hidden colors made clear, at the source's depth with its
color profile and resolution. It is a VectorCraft addition.
- The canvas (clip groups and white-shape opacity masks included),
printing and separations, PDF (soft masks of CMYK images included), SVG,
EPS, the raster formats, the legacy .ai and .ai editing data, EMF/WMF,
DXF and the flattener draw the masked pixels. A linked image stays
linked, and an output that would refer to its file writes the masked
pixels instead.
- Copy and paste, placing a document and placed documents carry the masked
pixels with the image, under the names they are given there.
- The dialog opens on the selected image's mask settings, with a Preview.
Without settings, the command returns them and changes nothing.
- Setting, removing and applying a mask are each one undo step, and no
step is added when nothing changes. Apply makes the masked pixels the
image's own and embeds a linked image. A mask that hides nothing is only
taken off, and the image keeps its pixels and its link.
- When an image's own pixels are replaced (Update Link, Relink, Crop
Image, a recolor, a linked file read again on open), its mask is made
again for the new pixels, in every art tree and in opacity-mask art.
Crop Image cuts the image's own pixels. When the new pixels can't be
masked, the mask keeps its settings and hides nothing.
- Objects in `ids` that are not images, CMYK images, linked images that
show their preview, images past the decode allowance, masks that would
hide every pixel, and grayscale images colorized with a process color
are refused. The Eyedropper, Object Mosaic, Create Gradient Mesh, Crop
Image's trim and Image Trace read the image's own pixels.
- An image is selected by a click anywhere in its frame, as before.
Remove Background now also refuses a grayscale image colorized with a
process color. Its result has an alpha channel, and a process color is
refused on a grayscale image with transparency.
Docs: docs/mcp.md (Image Tools add-on › Color Mask),
docs/target-app-parity.md.
effect.deferredStatus lists a stage in the art of a symbol, pattern, brush
or opacity mask by the id of the object in that art. effect.refreshDeferred
took such an id only while the object still had a deferred stage; an
existing object there without one was refused as no such object, while a
layer object without a stage was accepted. An id is now valid when it names
an object in the layers or in that art, an object without a stage adds
nothing to the counts, and an id of no object is an error.
The params text and docs/mcp.md said `id` names one object, but the params
text did not list the key, so `{id: N}` was refused as an unknown
parameter. It is now `{ids?|id?}`, and the command checks only the form of
the ids before it looks them up itself.
The requested ids are held in a set while the art is walked, and only those
with a stage are handed to the refresh.
The zero point dragged out of the box where the rulers meet snaps as a
Pen or shape tool's point does: with Smart Guides onto the anchors,
centres, edges and paths of the art and the artboards or into line with
them, with Snap to Point onto the anchors and along the ruler guides,
else onto whole pixels or the grid. The guides showing what it snapped
to show during the drag and go when it is released or cancelled
(Escape, focus loss, another document). The origin is still set once,
on release (document.setRulerOrigin, one undo step).
The engine snaps it (Session::ruler_origin_drag, with the tools crate's
rulerguide::OriginDrag over PointSnap::drawn), as it does a guide
dragged out of a ruler.
The Stroke panel menu's Edit Arrowheads… (stroke.arrowheads.edit) opens
Arrowheads.vectorcraft next to the preferences, creating it with one
example head when it is missing. Each symbol in the file is one head,
listed by name after the built-in heads in both arrowhead menus: it
points right, its registration point is the tip, and its height is drawn
as wide as the built-in heads. The file is read at startup and again
each time VectorCraft saves it; stroke.arrowheads.reload and
stroke.arrowheads.list read and list it.
Arrowhead gets a Custom variant, appended after the built-in names, that
carries the head's name and outline, so a document that uses one draws,
saves, reopens and exports it without the arrowheads file. stroke.set
takes {"custom": name} or the {"Custom": {name, path}} form that
document.inspect reports.
Part of #5421.
A new built-in add-on, Image Tools (Object › Plug-ins › Image Tools),
adds Remove Background… (`addon.image.removeBackground`). It makes the
background of the selected images transparent by lowering the alpha of
their own pixels, in one undo step, with no service or model. The
background is the most common color along the image's edges, the
average of the image's corners, or colors the user picks. Contiguous
(on by default) clears it only where it reaches in from an edge; off,
it is cleared wherever it is. Tolerance and Softness (0 to 100, 10 by
default) set how near a color must be to be cleared and the band over
which pixels fade out. Match measures that nearness as the RGB
distance or as the largest difference on one channel. It is a
VectorCraft addition.
- The image stays an image: an embedded PNG with alpha, the source's
ICC profile and resolution, and 16 bits or gray when the source is.
A linked image is embedded and keeps its Placement Options.
- When the edges hold at least as many clear pixels as pixels of their
most common color, edge mode clears nothing, so a second run leaves
the subject alone. Corner mode leaves clear corners out of the
average and clears nothing when all four are clear.
- With Match set to Each Channel, a tolerance of t / 255 × 100 clears
the colors within t 8-bit steps on every channel.
- A CMYK image, a linked image that shows its preview, an image that
would need more than the decode allowance (checked from its header)
and a result with no pixel left are refused, and the document stays
as it was. When no pixel changes, no undo step is added. The result
reports `cleared`, the pixels made fully clear.
- Picked colors are hex colors, with or without `#`, or black, white,
red, green or blue.
- The dialog opens with Preview off; with Preview on, the canvas shows
the result, and Cancel puts the image back. While the dialog
previews, the session keeps the images' decoded pixels (at most
128 MiB, 32 MiB in the web app) so each preview step does not decode
them again, and releases them when the dialog closes with OK or
Cancel. A run that is not a preview step keeps nothing.
Docs: docs/mcp.md (Image Tools add-on), docs/target-app-parity.md.
A library loaded from outside a confined client's read roots keeps its
`loaded/<file name>` id for the session, in ui.inspect, ui.docking and
the library commands, so an id taken from an earlier reply still finds
the same library after another library with the same file name is
loaded. swatch.library.copyToUser leaves out `path` when the file is
outside the read roots, as the saves do.
The ids come from the engine's concealed_library_ids, which now reads a
path written with either separator, counts another platform's absolute
path as outside the roots, and keeps each id in the session.
The grants that should be gone from grants.json but could not be removed
were kept per Session, so a second Session attached to the same Plug-ins
folder in the same process still counted them for plugin.inspect,
plugin.install and loading plug-ins. They are now kept for the process,
by the folder's canonical path and the plug-in id, and a successful
write of grants.json for the id in any session makes the grant count
again. Attaching another folder no longer clears them.
A failed removal does not override a write another session made for
the id in the meantime. The process keeps the latest 256 grant writes
for that check. Another process on the folder still reads grants.json
as it is written.
A script whose first lines name an engine with `#targetengine name` (or
`//@targetengine name`) runs in that engine, and a later script that names
it sees the globals and functions earlier runs left, until the engine is
dropped or the session ends. A script without the line, and
`#targetengine main`, still get a new engine for each run.
- A session keeps at most 8 named engines, each with a 64 MiB heap. A run
that goes past the heap ends with the heap limit, and the engine stays
usable. Names are 1 to 64 bytes. The line comes once, in the script
itself, before its code and its #include lines.
- An uncaught error, a limit or script.stop puts the documents back and
keeps the globals the run set. An internal error drops the engine. A run
stopped at a dialog or left paused ends where it was before the engine
is kept. An error in a function an earlier run defined is reported
without a line and column.
- `vc`, `$`, `alert`, `confirm` and `prompt` are made once for each engine
and act for the run that calls them, including copies an earlier run kept
in a global. `vc.args` gives a read-only copy of the calling run's
`args`, and what a run sets on `$` lasts for that run only.
- `script.engines` lists the named engines with their heap, and
`script.engine.drop {name}` drops one. Scripts cannot run either.
- The engines stay on the thread that made them. A session used on
another thread starts there with no named engines, and the old ones are
dropped on their own thread.
- The web build has no named engines: a run that names one other than
`main` ends with an error before any of it runs.
- `Realm::end` ends a run where it is, `Realm::error_source` counts from
the realm's last script, and `HostItem::FrozenGetter` gives a frozen
value each time it is read.
- The script checker lists `#targetengine` as a supported directive.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/gaps.md.
Ruler guides were a list kept beside the layers. Each guide is now an
object of its layer or sublayer, always directly in it and above the
layer's art. These behave as on main:
- Guides draw on screen only. Exports (SVG, PDF, PNG, PSD), Fit All,
Select All, the art bounds, clicks and marquees, and a layer's
Pathfinder and Transform effects leave them out. The Layers panel
lists them as guide rows of their layer, the highest guide.list
index first.
- Deleting a layer deletes its guides, and merging layers keeps their
guides in the merged layer.
- The guide commands number guides as guide.list does, in the order
they were made.
New: each guide takes an id from the document's object ids. An object
command given a guide's id fails with an error, and node.move cannot
put a guide into a group or out of its layers. A guide made while an
opacity mask, a symbol or a pattern is edited moves to the top
visible, unlocked layer when editing ends.
File format: native files still save the guides in the document's
guides list, in every file version, so older builds read them. Each
entry may now also carry the guide's id, name, locked and hidden
flags; older builds ignore those fields. Opening a file puts each
saved guide on its layer (on the top visible, unlocked layer when the
file names none or a layer it does not have), and each saved guide
counts against the object limit for opening a file. A ruler guide node
that a file holds below a layer's art moves up to the layer's guides,
and one anywhere else (in a group or another object, beside the
layers, or in a symbol) is removed. Ruler guides read from an .ai file
become guide objects on the layer they were on, numbered in the order
the file lists them.
File › Scripts now lists the .js and .jsx files of the Scripts folder next
to the preferences, each named after its file without the extension, with
each subfolder as a submenu, above Check Script… and Other Script…. The
help page describes this listing:
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/install-and-run-scripts.html
Choosing a listed script runs it through script.run without the warning
Other Script… shows. script.listInstalled returns the same listing, and
ui.runInstalledScript {path} runs one entry.
VectorCraft's own rules: the folder is read off the UI thread when the
app starts, when its window becomes active and when the File menu of the
window's menu bar opens; only names are read; links, junctions, hidden
and system entries are left out and checked again before a run; one read
stops at 2,048 entries, 512 scripts, 128 subfolders, 4 levels or 250 ms
and the menu then notes that some scripts were not listed. Under an
automation read root, script.listInstalled and ui.menu.list leave out a
folder outside the root.
#1183 (LloydNicholson) also proposed a Scripts menu listing a folder.
Scripts write to a new Script Console with $.write(...values) and
$.writeln(...values), each value as String(value) gives it. vc.log
entries, a run's uncaught error as name:line:column: message, and a
start, end and report line for each run go there too. The report line
gives the run's time, host calls and slowest commands.
- The console keeps at most 2,048 lines and 1 MiB of text, dropping the
oldest and counting what it dropped. A line is cut at 4,096 bytes and
a script's name at 256 bytes, with a count of cut lines. Line feeds,
carriage returns and CRLF break lines; other control characters show
as Unicode Control Pictures.
- script.run's result and scriptReport gain runTimeMs, timingAvailable,
hostCallCounts, slowestCommands and the run's own console. A failed
run's report has them too.
- script.console and script.console.clear read and empty the console;
ui.copyScriptConsole {first?, last?} copies lines as the panel shows
them.
- The panel lays out only the rows in view, selects with click and
Shift-click, and has Clear, Copy Selected and Copy All. Output goes on
while it is closed, and Clear works during a run.
Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
`Session::history_budget` (default `default_history_budget()`) caps the
memory the undo steps of all open documents keep together. After each
command, tool edit, interaction commit, direct edit, framed script run
and closed undo group, `Session::trim_histories` drops the oldest steps
of any document first, undo steps before redo steps. Each document keeps
its last eight undo steps and next eight redo steps, and a held history
keeps all of its steps. While a script runs or an undo group is open,
no steps are dropped, so a canceled group leaves the other documents'
steps in place. Closing a document frees its share. The steps are not
counted again while neither the budget nor any history changed. Each
document's own `byte_budget` stays as its ceiling.
edit.colorSettings set only the process-wide colour settings, so the RGB and
CMYK working spaces, intent and black point compensation went back to the
defaults at every start, although the profiles color.loadProfile loads are
kept (#2168, #2174). The engine now writes them to a settings file when they
change (Session::attach_color_settings), and the desktop app and
vectorcraft-cli keep that file as `Color Settings.json` next to the
preferences, attached right after the profile store so a saved working space
may be a loaded profile. Saved settings that can't be used (a profile that is
gone, a damaged or oversized file) leave the defaults with a log record, and
the next change writes over them. Nothing is read or written with
VECTORCRAFT_NO_PREFS.
Each command a script runs keeps a savepoint of the state it may need
to put back if it fails. The savepoint cloned every open document's
whole undo history, so a script that made N edits did O(N^2) work.
- The savepoint now clones each document's state without its history
and keeps a mark: the newest undo and redo steps and the serials of
the steps under them. Until the command ends, the history trims no
step, and a new step sets the redo steps aside instead of dropping
them. A failed command truncates both stacks back to the mark.
- file.close, file.closeAll, document.close and file.revert still keep
a whole copy, since they remove the history or the document.
- When a failed command removed or changed a step under the newest
one, the history cannot be put back exactly, and the run stops with
an error; its rollback restores the documents as they were before
the run.
- run_guarded no longer copies the histories during a script's run,
and a committed run moves the histories from before it back instead
of copying them.
- Symbol editing mode shares its saved redo steps between copies of a
document's state.
- A script cannot run edit.clearHistory. A run keeps each document's
undo steps from before it, so the command reported a clear that the
commit then undid.