Merge main into fix/aligned-paint-rows-of-design-art

This commit is contained in:
dotCooCoo
2026-11-14 19:10:09 -08:00
357 changed files with 26728 additions and 3919 deletions
+4 -4
View File
@@ -96,13 +96,13 @@ the two can still win that race.
| `ui.menu.list` / `ui.menu.invoke` | `{command, params}` | the full menu tree / invoke an item |
| `ui.contextMenu.list` | | the canvas context menu for the current selection, flattened like `ui.menu.list` (`path` holds its submenus). `ui.click {x, y, button: "right"}` on the canvas opens it, after selecting the object there unless it is already selected. With the Artboard tool it lists the artboard entries (see `docs/mcp.md`), after selecting the artboard there unless it is already selected. While a menu opened over locked objects is open, it has an `Unlock` submenu with one item for each locked object under the pointer, topmost first and at most 20, labeled with the object's name, which runs `layer.setProps {ids: [id], locked: false}`. A locked group gets one item, and the locked objects inside it get none. Hidden objects, objects on hidden, locked or template layers, and in isolation mode objects outside the isolated group get no item |
| `ui.tool.select` / `ui.tool.list` | `{tool}` | |
| `ui.pointer` | `{events:[{kind: down|drag|up|move|doubleclick, x, y, space?: "doc"|"screen", mods?}]}` | drive the active tool exactly like the mouse: `mods.cmd` held at a press with any tool but a selection tool drags with the selection tool chosen last (Direct Selection with the Pen until one is), and the release gives the tool back as it was; the reply lists `selection`, `key` (the key object, null without one) and `tool`. With the Shape Builder or the Live Paint Bucket and a selection of more than 128 segments, the app finds the regions off the UI thread, so a `move` replies before the region under the pointer is highlighted, and the highlight shows in the first frame after the regions are found. With the Shape Builder and such a selection, the app also merges off the UI thread: the reply to the events that end a click or drag comes once the merge is applied. A click or drag made before the regions are found does not wait for them; the app records it and makes it once they are found. Until then the canvas keeps drawing, with the touched regions highlighted and the busy pointer. A click or drag sent meanwhile, with `mods.cmd` too, and the Shape Builder's arrow keys are made after the merge in the order sent, and the reply to such a request comes once its events are made and any merge they start is applied; a request of `move` events alone is answered at once. A command that can edit, select, save or redraw the documents (an edit, an undo, a selection, a save or an export, a tool option, another tool, another document, closing, a dialog's preview, a menu item, a command of the app's own such as `ui.recolorDialog`, the proof and separations views, a preference, added fonts) waits for the merge and the input sent before it, and runs after them; queries that only read, such as `document.inspect`, run at once on the document before the merge |
| `ui.pointer` | `{events:[{kind: down\|drag\|up\|move\|doubleclick\|tripleclick, x, y, space?: "doc"\|"screen", mods?}]}` | drive the active tool exactly like the mouse (`doubleclick` follows the second click's `up`, and `tripleclick` the third's): `mods.cmd` held at a press with any tool but a selection tool drags with the selection tool chosen last (Direct Selection with the Pen until one is), and the release gives the tool back as it was; the reply lists `selection`, `key` (the key object, null without one) and `tool`. With the Shape Builder or the Live Paint Bucket and a selection of more than 128 segments, the app finds the regions off the UI thread, so a `move` replies before the region under the pointer is highlighted, and the highlight shows in the first frame after the regions are found. With the Shape Builder and such a selection, the app also merges off the UI thread: the reply to the events that end a click or drag comes once the merge is applied. A click or drag made before the regions are found does not wait for them; the app records it and makes it once they are found. Until then the canvas keeps drawing, with the touched regions highlighted and the busy pointer. A click or drag sent meanwhile, with `mods.cmd` too, and the Shape Builder's arrow keys are made after the merge in the order sent, and the reply to such a request comes once its events are made and any merge they start is applied; a request of `move` events alone is answered at once. A command that can edit, select, save or redraw the documents (an edit, an undo, a selection, a save or an export, a tool option, another tool, another document, closing, a dialog's preview, a menu item, a command of the app's own such as `ui.recolorDialog`, the proof and separations views, a preference, added fonts) waits for the merge and the input sent before it, and runs after them; queries that only read, such as `document.inspect`, run at once on the document before the merge |
| `ui.key` / `ui.text` | `{key, shift?, alt?, cmd?}` / `{text}` | synthetic keyboard input |
| `ui.move` | `{x, y}` | screen-point pointer move |
| `ui.click` / `ui.drag` | `{x, y, button?, count?, shift?, alt?, ctrl?, cmd?}` / `{x, y, toX, toY, steps?, count?, button?, shift?, alt?, ctrl?, cmd?}` | egui pointer input; see synthetic input budgets above |
| `ui.wheel` | `{x, y, dy?, dx?, unit?: "line"\|"point", shift?, alt?, cmd?}` | a mouse wheel turn over screen point (x, y): `dy` notches up (+) or down, `dx` sideways. Over the canvas the wheel scrolls, Cmd-wheel (Ctrl on Windows and Linux) scrolls sideways and Alt-wheel (Option on the Mac) zooms about the pointer (with the `ctrlWheelScrollsSideways` preference off, Cmd-wheel zooms too); with the `zoomWithMouseWheel` preference the wheel and Alt-wheel zoom about the pointer, Shift-wheel scrolls up and down and Cmd-wheel (Ctrl on Windows and Linux) sideways. Over a focused numeric field (click it first) each notch steps its value as Up/Down do (Shift: ten, Cmd/Ctrl: a tenth) and the panel stays put; over anything else in a panel the wheel scrolls it. Over the canvas, the wheel preferences in `docs/mcp.md` change what these keys do |
| `ui.set` | `{brightness?, panel?, rulers?, outline?, grid?, smartGuides?, boundingBox?, controlBar?, imageTracePreview?}` | `outline`: View › Outline for the active document only (each document has its own, a document opens in Preview, and `ui.inspect` shows it as `view.outline`; an error without a document). `imageTracePreview`: the Image Trace panel's Preview (`ui.inspect` shows it as `ui.image_trace_preview`) |
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value, action?}` or `{fields: [{field, value}…], action?}` | fill and submit the open dialog. `ui.dialog.set` takes the fields the dialog has (`ui.inspect`) other than its `__` fields, the `__` fields that are controls of the dialog (such as `__section` and `__allArtboards` in Save PDF, `__constrain` in Transform Each and the shape dialogs, and `__search` and `__category` in Preferences; Save PDF's and Print's `__savePresetAs`, the preset name, only while Save Preset… asks for it, since OK then saves a preset), `discard` on a dialog with a discard button, `preview` on one with a live preview, the fields that the dialog reads but adds only when a control changes (such as `searchFolder`, or `copy` in the Transform dialogs), and the fields it passes to the command it runs: every parameter of the `command` dialog's command (`ui.paramDialog`), and the `textureImage` that Load Texture… sets in the Glass and Texturizer effects; any other field is an error that lists the dialog's fields, and the dialog stays as it was. Any other `__` field, held or not, holds what the dialog's OK acts on (such as the `__command` and `__params` of a `confirm` or `command` dialog) or the dialog's own state: setting it is the error "the `<kind>` dialog cannot set protected field `<field>`", and the dialog stays as it was. `fields` sets the list (at most 256 items) in order (a missing `value` is `null`) and checks every item first: when one is refused, the call is an error and no field changes. `action: "confirm"` or `"cancel"` on `ui.dialog.set` then confirms or cancels the same dialog in the same request, as `ui.dialog.confirm` and `ui.dialog.cancel` do, and the reply is `{dialog, result}`: the dialog after the fields were set and the reply to confirm (`null` for cancel); any other `action` is an error and no field changes |
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value, action?}` or `{fields: [{field, value}…], action?}` | fill and submit the open dialog. `ui.dialog.set` takes the fields the dialog has (`ui.inspect`) other than its `__` fields, the `__` fields that are controls of the dialog (such as `__section` and `__allArtboards` in Save PDF, `__constrain` in Transform Each and the shape dialogs, and `__search` and `__category` in Preferences; Save PDF's and Print's `__savePresetAs`, the preset name, only while Save Preset… asks for it, since OK then saves a preset), `discard` on a dialog with a discard button, `preview` on one with a live preview, the fields that the dialog reads but adds only when a control changes (such as `searchFolder`, or `copy` in the Transform dialogs), and the fields it passes to the command it runs: every parameter of the `command` dialog's command (`ui.paramDialog`), and the `textureImage` that Load Texture… sets in the Glass, Texturizer and Rough Pastels effects; any other field is an error that lists the dialog's fields, and the dialog stays as it was. Any other `__` field, held or not, holds what the dialog's OK acts on (such as the `__command` and `__params` of a `confirm` or `command` dialog) or the dialog's own state: setting it is the error "the `<kind>` dialog cannot set protected field `<field>`", and the dialog stays as it was. `fields` sets the list (at most 256 items) in order (a missing `value` is `null`) and checks every item first: when one is refused, the call is an error and no field changes. `action: "confirm"` or `"cancel"` on `ui.dialog.set` then confirms or cancels the same dialog in the same request, as `ui.dialog.confirm` and `ui.dialog.cancel` do, and the reply is `{dialog, result}`: the dialog after the fields were set and the reply to confirm (`null` for cancel); any other `action` is an error and no field changes |
| `ui.screenshot` | `{path?, data?}` | capture the window (PNG), written to `path`; `data: true` sends it back as `pngBase64` too. The inline PNG is capped at 64 MiB: with a `path` the file is still written and `pngBase64` is omitted when the image is larger, while a path-less `data: true` request fails if either the raw RGBA image or the encoded PNG exceeds the cap. Needs a presented frame: with the screen locked or the window minimized/covered it fails after ~8 s with an explanatory error |
| `ui.render` | `{path?, scale?, artboard?, data?, traceViews?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first; `traceViews: true`: Image Trace objects draw in their View, as on the canvas, instead of their tracing result): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
| `ui.render` | `{path?, scale?, artboard?, data?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
@@ -122,8 +122,8 @@ the two can still win that race.
| Native file dialogs | any command with no `path` that needs one (`file.open`, `file.saveAs`, `file.place`, exports…) | on macOS (a sheet on the window) and Linux the dialog doesn't hold the window: the call returns `"cancelled"` at once, `ui.inspect` reports `fileDialog`, and when the person at the computer picks a file the command runs again on the document it was asked for. While it is open another dialog is refused: that call returns `"cancelled"` too, and the status bar (`ui.inspect` → `ui.status`) says `"A file dialog is open: choose there first"`; on macOS `app.quit`, `file.close` and `file.closeAll` return that message and the menu bar does nothing (#592, #867). On Windows the dialog is shown in line: the call returns once it closes. Pass `path` to skip the dialog |
| Background Save / Export | `app.save`, `app.export {path}`, `file.save`, `file.saveAs`, `file.saveCopy`, `file.exportForScreens {folder, …}` | with the `backgroundSave` / `backgroundExport` preferences on (the default) the desktop app writes the file from a snapshot on a worker thread: the call returns `{path, background: true, status}` at once (`file.exportForScreens`: `{background: true, status}`, and the files are written into the folder from a snapshot taken at the call; an `app.export` given an option the format doesn't read: also `warnings`, naming the option), `ui.inspect` lists the jobs still running in `background` (labels such as `"Saving a.vectorcraft"`), and the document counts as saved (as of the snapshot) once its file is written. Quit and Close wait for them. A job that fails reports the error in the status bar only (`ui.inspect` → `ui.status`), a path outside `--automation-write-root` included. With the preferences off (`prefs.set`), saves return once written. `app.save {path?, wait: true}` and `app.export {path, wait: true}` wait for the jobs already running, write the file before they reply whatever the preferences are set to, and return its result or the error. When the save asks first (the question before replacing a file that opened with losses, or on macOS and Linux the file dialog for an untitled document without `path`), the reply is `{pending}` or `"cancelled"` as before, and the save that runs after the answer uses the preferences. A `wait` that is not `true`, `false` or `null` is refused with "`wait` must be true or false", and nothing is written |
| `engine.execute` `file.exportForScreens` | `{}` / `{…document.exportForScreens params}` | without params opens `exportForScreens` on the settings the document last exported with (`document.exportSettings`). While the Artboard tool is in use, the artboards it has selected (its `selected` option, the rows the Artboards panel highlights) are checked instead, and `select` is `range`, or `all` when every artboard is selected. Fields: `tab` (`artboards` \| `assets` \| `layers` \| `selection`; OK reads Export Layer or Export Selection on those), `layers` ([layer id…] checked), `selectionMode` (`one` \| `each`), `name` (the file name pattern of `document.exportForScreens`), `counterStart`, `folders` (`none` \| `rows` \| `format` \| `item` \| `itemFormat`), `exportPreset` (the preset applied last), `__savePreset` (the name typed to save one), `select` (`all` \| `range` \| `full`), `range` ("1-3, 5"), `boards` (a bool per artboard; checking them sets `select` and `range`), `includeBleed`, `folder` (desktop; default the Desktop, else home), `openLocation`, `subfolders`, `prefix`, `preset` (`""` \| `mobile` \| `density`), `formats` (rows `{format, scale: "2x" \| "100w" \| "100h" \| "72ppi", suffix, quality?, folder?}`), `settings` (`{png \| png8 \| jpg \| webp \| gif \| svg \| pdf: {…options}}`, the gear's Format Settings; `__settings` names the format shown). `ui.dialog.confirm` exports (a bad range or no folder keeps it open); on the web OK reads "Download" and the files download (several as one `.zip`). With params it runs `document.exportForScreens`: no `folder` returns the files (or with `zip: true` one zip) as `dataBase64`; with `openLocation` the first file is shown in the file manager. When `warnings` is not empty, an export into a folder or a download shows the first in the status bar as Save As shows a save's notes |
| Export for Screens' Assets tab, Asset Export panel | `ui.dialog.set` `tab: "assets"`, `assets: [asset id…]`; `window.panel {panel: "assetExport"}` | on the Assets tab of `exportForScreens` the `assets` field lists the checked asset ids (all of them when it opens); `ui.dialog.confirm` runs `document.exportForScreens {assets}` (nothing checked keeps it open) and OK reads "Export Asset". The Asset Export panel acts through `assets.*` and shares the format rows with the dialog (`assets.settings.set`). The dialog has the format presets menu (☰). In the dialog, `exportPreset` names the preset applied last (`ui.dialog.set` of it applies the preset it names, as picking it does; a name that is not a preset's is an error and leaves the dialog as it was), `__exportPresets` lists the presets, and `__savePreset` holds the name being typed after Save Preset…. The panel menu applies presets through `assets.settings.set {exportPreset}` and holds Save As Preset…, Save Changes, Delete Preset, Import Preset… and Export Preset…. Import and Export use picked files. |
| `engine.execute` `file.exportSelection` | `{multiple?}` | File › Export Selection…: collects the selected objects as one asset (`assets.add`; one per object with `multiple: true`) and opens `exportForScreens` on its Assets tab with just them checked → `{assets}`. `document.exportSelection` with params still exports the selection in one go, without the dialog; with no params or `{}` the save panel picks the file and its format's options dialog follows, as after Export As, and the call returns `null` while the dialog is open (OK returns what `file.exportAs` returns, without `format` or `bounds`). `file.exportAs {path, exportSelection: true, …options}` writes the selection without a dialog |
| Export for Screens' Assets tab, Asset Export panel | `ui.dialog.set` `tab: "assets"`, `assets: [asset id…]`; `window.panel {panel: "assetExport"}` | on the Assets tab of `exportForScreens` the `assets` field lists the checked asset ids (all of them when it opens); `ui.dialog.confirm` runs `document.exportForScreens {assets}` (nothing checked keeps it open) and OK reads "Export Asset". The Asset Export panel acts through `assets.*` and shares the format rows with the dialog (`assets.settings.set`). The dialog has the format presets menu (☰). In the dialog, `exportPreset` names the preset applied last (`ui.dialog.set` of it applies the preset it names, as picking it does; a name that is not a preset's is an error and leaves the dialog as it was), `__exportPresets` lists the presets, and `__savePreset` holds the name being typed after Save Preset…. The panel menu applies presets through `assets.settings.set {exportPreset}` and holds Save As Preset…, Save Changes, Delete Preset, Import Preset… and Export Preset…. Import and Export use picked files. Its Create Sub-folders and Open Location after Export run `assets.settings.set {subfolders}` and `{openLocation}`; its Format Settings… opens the dialog on the Assets tab with `__settings` set to the first row's format. |
| `engine.execute` `file.exportSelection` | `{multiple?}` | File › Export Selection…: collects the selected objects as one asset (`assets.add`; one per object with `multiple: true`) and opens `exportForScreens` on its Assets tab with just them checked → `{assets}`. `document.exportSelection` with params still exports the selection in one go, without the options dialog (without `path`, the save panel picks the file first); with no params or `{}` the save panel picks the file and its format's options dialog follows, as after Export As, and the call returns `null` while the dialog is open (OK returns what `file.exportAs` returns, without `format` or `bounds`). `file.exportAs {path, exportSelection: true, …options}` writes the selection without a dialog |
| CSS Properties panel | `window.panel {panel: "cssProperties"}`; `engine.execute` `css.copy` / `css.exportFile` | the panel shows `css.selection` of the selection (after Generate CSS, while nothing is selected, `css.generate`); its menu holds the options the commands take (`units`, `position`, `dimensions`, `unnamed`, `rasterize`). `css.copy {scope?, …options}` copies the CSS to the clipboard → `{css, rules}`; `css.exportFile {path?, scope?, …options}` writes `css.export` to `path` (else a picked file; the web downloads it) with the pictures of rasterized art next to it → `{path, rules, images, warnings}`, and the status bar shows `warnings` as Export As shows an export's notes |
| `ui.dialog.*` on `importPdf` | `{field: "page" \| "allPages" \| "range" \| "cropTo" \| "password", value}` | opening (`app.open`, `file.open`) a PDF, `.ai` or `.ait` with a password, or one with several pages unless it is a `.ai` or `.ait` with its editing data or carries its VectorCraft document (those open whole), or placing (`file.place` without `page`) any of them with several pages or a password, opens the Import PDF dialog (`mode`: `open` or `place`; `pageCount`). While `__locked` is true, set `password` and `ui.dialog.confirm` to unlock it (a one-page file then opens or is placed). Then set `allPages: false` and `range` ("2-3, 5") to open some pages, or `page` to place one, and `cropTo` (`bounding`, `art`, `crop`, `trim`, `bleed`, `media`); `ui.dialog.confirm` opens or places them (placing keeps the other `file.place` params, such as `link`, `template` and `at`, in `__place`). `document.open {pages, cropTo, password}` and `file.place {page, crop, password}` do the same without the dialog |
+1 -1
View File
@@ -297,7 +297,7 @@ other `sv-*` locale resolve to it), Georgian (`ka`, complete; `ka-GE` and `ka_GE
placeholders are translated in turn (the reason after `: {e}` is often a message too). A literal brace in a
row is written `{{` or `}}`, in the English and in the translation: a message made with `format!` keeps the
doubled braces of its format string, and a plain string literal has each of its braces doubled, so the row for
`"expected {\"objects\": [...]}"` is `expected {{"objects": [...]}}`. Czech, German, Greek, Spanish, French, Georgian, Hindi, Hungarian, Italian, Japanese, Korean, Polish, Brazilian Portuguese, Russian, Simplified Chinese, Traditional Chinese, Swedish, Ukrainian, Vietnamese, Finnish, Turkish, Catalan and Dutch
`"expected {\"objects\": [...]}"` is `expected {{"objects": [...]}}`. Czech, German, Greek, Spanish, French, Georgian, Hindi, Hungarian, Italian, Japanese, Korean, Polish, Brazilian Portuguese, Russian, Simplified Chinese, Traditional Chinese, Swedish, Ukrainian, Vietnamese, Finnish, Turkish, Catalan, Dutch and Danish
translate status and error messages (`COMPLETE_MESSAGES` in `i18n::tests`); the untranslated report lists the
message literals the scan finds, and the phrases a parameter schema's error builds at run time
(`SCHEMA_PHRASES`), that one of them has no row for. The scan covers the crates in `MESSAGE_CRATES`, the
+1 -1
View File
@@ -27,7 +27,7 @@ Part of [target-app-parity.md](target-app-parity.md); open work is in [gaps.md](
| Pathfinder | Add, Intersect, Exclude, Subtract, Minus Back, Divide, Trim, Merge, Crop, Outline, Hard Mix, Soft Mix, Trap | 13/13 | Trap traps only solid fills (gradients and patterns hide what is below them but don't trap) and spreads each colour's visible area as a whole; the public page gives no default values, so Trap's 0.25 pt, 100% and 40% are VectorCraft's own |
| Rasterize | Rasterize… (as a live effect) | 1/1 | — |
| Stylize | Drop Shadow, Feather, Inner Glow, Outer Glow, Round Corners, Scribble | 6/6 | Scribble Options' Settings presets |
| SVG Filters | Apply SVG Filter… and the filter list | 1/1 | The submenu lists the document's own filters: VectorCraft ships no default filters. A filter that is the object's last effect is written to SVG as a `<filter>` element; otherwise SVG export draws the object as an image. A filter on a turned or skewed object in an imported SVG is left out with a warning |
| SVG Filters | Apply SVG Filter… and the filter list | 1/1 | The submenu lists 14 default filters and then the document's own; the public page names no default set, so the set, names and values are VectorCraft's own, and a default is copied into the document when it is applied or edited. A filter that is the object's last effect is written to SVG as a `<filter>` element; otherwise SVG export draws the object as an image. A filter on a turned or skewed object in an imported SVG is left out with a warning |
| Warp | Arc, Arc Lower, Arc Upper, Arch, Bulge, Shell Lower, Shell Upper, Flag, Wave, Fish, Rise, Fisheye, Inflate, Squeeze, Twist | 15/15 | warp point editing in envelopes; each style bends the bounds as one Bézier patch (`version` 1), so Arc's edges are cubic curves close to concentric arcs; warps saved without a version keep the earlier map, where Arc is a circular arc in the object's own proportions (#1578) |
Document Raster Effects Settings exists. When the 64 million pixel cap on an effect image lowers
+5 -4
View File
@@ -245,7 +245,8 @@ private editing data. VectorCraft:
group is tagged once and paints its marks with one fill and one stroke, which the graph keeps as that series'
paint; Graph Data then edits it. Other graphs (pie and scatter graphs, graphs with designs or
customizations, records of later versions) come in as plain groups, paths and type, with a note. Saving a graph to
`.ai`, in either form, writes its art without its data or settings, with a warning.
`.ai`, in either form, writes these records around its art when the reader above reads them back as the same graph,
so the file opens again with its data and settings; any other graph is written as its art, with a warning.
Legacy `.ai` patterns in the version 7 form (a list of `@` and `&` layers, `_ &` for the tile's background)
open as pattern swatches, with a fill's offset, scale, rotation, reflection and shear applied before its
matrix. Legacy `.ai` linked images in
@@ -281,7 +282,7 @@ Names in the editing data that aren't UTF-8 (layers, objects, artboards, colors,
| VectorCraft `.vectorcraft` / `.vctemplate` | — | yes | yes | lossless JSON, versioned, save down to v1; property-tested round trips (`format/tests/prop_format.rs`); documents with a lot of path data are written as compact JSON, about a fifth of the size (#1492); a save falls back to a plain fsync and to writing elsewhere and copying in when the folder refuses the usual steps (#1625, #1649, part of #1620; not confirmed on a real network share); the size limits on embedded data also apply to files read from memory (#1783), and saving over a private file on Unix leaves no readable temporary copy beside it (#1778); a dynamic symbol's type (`dynamic` on the symbol) and an instance's appearance changes to its art (`symbol_overrides`, each path at most 64 deep, counted against the object budget) are written only when set, so documents without them save as before (#4938) |
| Illustrator template `.ait` | open, save | yes | PDF-compatible only, from Save as Template (`.vctemplate` stays its default) | New from Template opens a saved `.ait` untitled with its layers and art (`tests_pdfedit.rs`) |
| Illustrator `.ai` | open, save | yes (PDF-compatible + editing-data structure with template layers and ruler guides, File Info) | PDF-compatible, or legacy version 7 (layers and groups) | see above; no real-file corpus |
| PDF | open, place, save | yes (authored ActualText in editable import, #6165; layers, masks, editable text, security, overprint, File Info from the XMP packet and the document information dictionary; Crop To › Trim takes the bleed box as the document's bleed) | yes (presets, PDF/X-1a/3/4, layers, marks and bleed, ICC, fast web view, 1.3+; objects' URLs as link annotations, not in PDF/X) | fuzzed import; shared guarded writer and windowed reader ([pdf-backend.md](pdf-backend.md)), metadata, incomplete-content and dependency-source regressions; PDF text opens where the file draws it, glyph by glyph (#722: kerning or none, spacing per character, text rise as baseline shift, vertical writing, justified text boxes); alpha soft masks drawn as images import with the mask (#1551); a clip only counts as covering the page when its shape does (#1542); a PDF another app copied pastes as editable vector art (#1499); letter-spaced type on a path keeps its length and its glyph-by-glyph stroke comes in as one text object (#1586, #1585); a crafted name tree with shared nodes no longer takes exponential time on open (#1781); fonts named with hyphens (`Noto-Sans-Bold`) or with a composite font's CMap (`Name-Identity-H`) find the installed family; invisible text (rendering mode 3: a scanned page's recognized text) opens as type with no fill or stroke and saves back as invisible, searchable text (#2675); PDF/X-1a and X-3 flattening in a CMYK document keeps CMYK inks where it rasterizes (#2160); an image mask (`/ImageMask true`) painted in a solid color opens as a grayscale image colorized by that color, a spot color keeping its swatch and tint, so Fill and Recolor Artwork reach it, and the same mask painted again in another color keeps that color (#6313); an image in a Separation space opens as a grayscale image of its tints colorized by its ink at 100 % (a spot swatch, a process plate or Registration), its soft mask as its opacity mask (#6314); a DeviceN image of one colorant opens the same way, and one of process plates only (the ink `None` may be among them) opens in CMYK with each colorant's tint in its plate (#6348) PDF export keeps harakat and zero-advance Noto Arabic dot glyphs in one compatible batch per layout line, with per-glyph vertical offsets (#6309). `crates/pdf/src/tests/realtext.rs` (`mark_text_*`) checks decoded cluster mappings, mixed Arabic/Latin/digits, contour placement against outline export, vertical columns, differing path rotations and PDF/X-1a/3/4. Editable-text import preserves authored ActualText separately (#6165), with cross-line/style layout limits; separately styled or differently rotated clusters and external viewer search/copy remain outside this export-only evidence. |
| PDF | open, place, save | yes (authored ActualText in editable import, #6165; layers, masks, editable text, security, overprint, File Info from the XMP packet and the document information dictionary; Crop To › Trim takes the bleed box as the document's bleed; Import PDF pages as links opens each page as a placed document linked to it, on its own artboard, `tests/placed_document.rs`) | yes (presets, PDF/X-1a/3/4, layers, marks and bleed, ICC, fast web view, 1.3+; objects' URLs as link annotations, not in PDF/X) | fuzzed import; shared guarded writer and windowed reader ([pdf-backend.md](pdf-backend.md)), metadata, incomplete-content and dependency-source regressions; PDF text opens where the file draws it, glyph by glyph (#722: kerning or none, spacing per character, text rise as baseline shift, vertical writing, justified text boxes); alpha soft masks drawn as images import with the mask (#1551); a clip only counts as covering the page when its shape does (#1542); a PDF another app copied pastes as editable vector art (#1499); letter-spaced type on a path keeps its length and its glyph-by-glyph stroke comes in as one text object (#1586, #1585); a crafted name tree with shared nodes no longer takes exponential time on open (#1781); fonts named with hyphens (`Noto-Sans-Bold`) or with a composite font's CMap (`Name-Identity-H`) find the installed family; invisible text (rendering mode 3: a scanned page's recognized text) opens as type with no fill or stroke and saves back as invisible, searchable text (#2675); PDF/X-1a and X-3 flattening in a CMYK document keeps CMYK inks where it rasterizes (#2160); an image mask (`/ImageMask true`) painted in a solid color opens as a grayscale image colorized by that color, a spot color keeping its swatch and tint, so Fill and Recolor Artwork reach it, and the same mask painted again in another color keeps that color (#6313); an image in a Separation space opens as a grayscale image of its tints colorized by its ink at 100 % (a spot swatch, a process plate or Registration), its soft mask as its opacity mask (#6314); a DeviceN image of one colorant opens the same way, and one of process plates only (the ink `None` may be among them) opens in CMYK with each colorant's tint in its plate (#6348) PDF export keeps harakat and zero-advance Noto Arabic dot glyphs in one compatible batch per layout line, with per-glyph vertical offsets (#6309). `crates/pdf/src/tests/realtext.rs` (`mark_text_*`) checks decoded cluster mappings, mixed Arabic/Latin/digits, contour placement against outline export, vertical columns, differing path rotations and PDF/X-1a/3/4. Editable-text import preserves authored ActualText separately (#6165), with cross-line/style layout limits; separately styled or differently rotated clusters and external viewer search/copy remain outside this export-only evidence. |
| EPS | open, place, save | yes (own PostScript L3 interpreter, Illustrator EPS editing data structure) | yes (L2/L3, TIFF previews) | CIDFonts the file doesn't carry not drawn, but for those over a predefined CMap (`UniJIS-UCS2-H`, `90ms-RKSJ-H`, `GBK-EUC-H`, `KSCms-UHC-H`, `ETen-B5-H`), set as type in the font of the CIDFont's name, vertical over a `-V` CMap; a name that ends in the row-and-cell CMap `H` or `V` splits off the CMap only when the file defines the CIDFont of that name, the CIDFont is `Ryumin-Light` or `GothicBBB-Medium` (the Japanese CIDFonts a printer carries), or the name has a hyphen and an installed face that covers Japanese has it; name-keyed fonts in a FontSet set as type by name; #505; images encoded with `CCITTFaxDecode` (ITU-T T.4 Group 3 and T.6 Group 4, with `K`, `Columns`, `Rows`, `EndOfBlock`, `EndOfLine`, `EncodedByteAlign` and `BlackIs1`) draw, and damaged data keeps the rows decoded before its first damaged row; Type 42 (TrueType) fonts set type at its size (#1553); an embedded Type 1 font's encrypted part is read for its glyphs, which space its type, outline it and draw glyphs without a character, in plain and composite fonts; a Type 42 font that carries its TrueType font gives each code its character by its glyph name or, for names that say nothing (`/g12`), by the glyph's entry in the font's own character map, spaces its type by its own widths and draws glyphs that stand for no character from its outlines; the show operators' spacing along the baseline (`ashow`, `widthshow`, `xshow`, `yshow`, `xyshow`, and the moves of `kshow`'s procedure, run between each two characters) is kept on live type as tracking, so type in a font that isn't installed keeps the file's line lengths (spacing that moves characters off the baseline, as `yshow`, `xyshow` or a `kshow` procedure can, places each character as its own text object); composite (Type 0) fonts with the 8/8 mapping, as PDF-to-PostScript converters write two-byte fonts, draw a Type 42 descendant's glyphs from the TrueType outlines it carries and an embedded Type 1 descendant's from its charstrings, and set other descendants' characters as type at the composite font's size (other mappings are still set as type in the font of their name); CID-keyed fonts (`composefont`, `FMapType` 9 over a CMap built with the CIDInit operators or `Identity-H`/`Identity-V`; `bfchar` and `bfrange` mappings give a base-font descendant its codes or glyph names) draw the glyphs of the Type 0 CIDFonts (Type 1 charstrings in their `StartData` binary data or `GlyphDirectory`) and Type 2 CIDFonts (TrueType glyphs through `CIDMap`) they carry, and the CID-keyed fonts of a FontSet's Compact Font Format data (Type 2 charstrings); a clip only counts as covering the page when its shape does (#1542); an EPS another app copied pastes as editable vector art (`clipboard.importEps`, #1499); a stitched shading keeps its hard colour changes on import (#1654); sampled functions (type 0) interpolate between their samples, multilinearly or by cubics with `Order 3`, and a sampled axial or radial gradient has a stop at each sample; a gradient the page paints as a row of abutting one-colour strips (as Level 2 writers draw smooth shades) imports as one linear gradient, without the seams the strips leave when each is drawn alone; CMYK images, CMYK JPEGs included, keep their ink amounts on import, as from PDF, and the pixels a `MaskColor` or `ImageType 3` mask hides become a grayscale opacity mask; ruzstd 0.9.1 for compressed editing data (#1807, RUSTSEC-2026-0344) |
| PostScript `.ps` | open, place | yes (the EPS reader reads its first page; `.epsf` files open and place as EPS) | via Print to PostScript | `tests_epsimport.rs` |
| DCS 1.0 / 2.0 | place linked | place linked only (one image of the plates; spot plates as spot swatches; the composite's clipping path; each plate on its own ink in Separations Preview and print separations) | no | `crates/eps/src/dcs/tests.rs`, `crates/engine/src/tests/dcs.rs` |
@@ -290,8 +291,8 @@ Names in the editing data that aren't UTF-8 (layers, objects, artboards, colors,
| HP-GL/2 `.plt`, `.hpgl`, `.hgl`, `.hpg` | — | yes (pen moves, pens and widths, line types and attributes, circles, arcs, wedges and Bézier curves, filled and edged rectangles, wedges and polygons, encoded polylines, scaling and rotation; labels, symbol mode, raster images and hatching aren't drawn) | yes (`.plt`: each visible path once along its line, a pen per color or a pen table, type as outlines, clipping applied, HP-GL or HP-GL/2 with pen colors and encoded polylines) | recognized by its first instructions after any printer escapes, under its own extensions or one no other format reads; `crates/cad/src/hpgl/tests.rs`, `crates/cad/src/hpgl/write/tests.rs` (plots read back by the reader), `tests_hpgl.rs`, `import_fuzz.rs` |
| DWG | open, export | no | no | no open spec; DXF instead |
| EMF / WMF | open, export | yes | yes | an oversized embedded PNG or JPEG is refused from its header, before any pixels are decoded (#1776) |
| PSD | open, place, export | yes (open: layers as layers, or the merged image with `layers: false`; place: merged image; every mode and depth; `.psd` and `.pdd`) | yes (layered, max editability) | placing as layers and a PSD import options dialog missing; the embedded ICC profile's tags are 4-byte aligned (#1584) |
| PSB | open | yes (layers as layers, or the merged image) | no | |
| PSD | open, place, export | yes (open: layers as layers, or the merged image with `layers: false`; place: merged image; the merged image comes in clipped by the file's clipping path; every mode and depth; `.psd` and `.pdd`) | yes (layered, max editability) | placing as layers and a PSD import options dialog missing; the embedded ICC profile's tags are 4-byte aligned (#1584) |
| PSB | open | yes (layers as layers, or the merged image; the merged image comes in clipped by the file's clipping path) | no | |
| PNG / JPEG / GIF / BMP / TIFF / Targa | open, place, export | yes (PNG: `.png` and `.pns`; BMP: `.bmp`, `.dib` and `.rle`; Targa: `.tga`, `.vda`, `.icb` and `.vst`, raw or run-length encoded, with a 16-bit file's one-bit alpha) | yes; TIFF is also selectable in Export for Screens and Asset Export with its existing options | Targa: `tests_tiffbmp.rs`, `tests/links.rs`; TIFF: grayscale with an alpha channel or other extra channels reads (#1466 in part), and so do files that store each channel as its own plane (PlanarConfiguration 2), CMYK ones with their ink amounts, and 1-bit files compressed with CCITT Group 3 (Compression 2 and 3, one- and two-dimensional rows); the ICC profiles embedded in JPEG and TIFF have 4-byte aligned tags (#1584); PNG export marks its colours as sRGB (`embedIcc`, an `sRGB` chunk; no ICC profile is embedded, #2221) |
| WebP | export (Export for Screens) | yes | yes (lossless only; lossy written lossless with a warning) | |
| JPEG XL `.jxl` | — | yes (first frame; 16-bit and floating-point samples as 8 bits, alpha kept; CMYK refused) | no | decoded with jxl-oxide; the size is checked from the header before decoding; container Exif resolution sizes opened and placed images; tests in `crates/engine/src/cmd/fileio/tests_jxlread.rs` and `crates/engine/tests/import_fuzz.rs` |
+1
View File
@@ -522,6 +522,7 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi
## G2 Photoshop-style raster effects and the Effect Gallery
- **Done:** Effect › Texture › Texturizer › Load Texture… (stored grayscale `textureImage`, Loaded Texture in the Texture menu), matching Glass.
- **Done:** Effect › Artistic › Rough Pastels › Load Texture… ([raster effects reference](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/apply-filter-effects/raster-effects-reference.html)): the image's gray levels are the paper surface, stored with the effect, listed as Loaded Texture in the Texture menu, with Scaling, Relief, Light Direction and Invert acting on it.
- **Done:** the Effect Gallery's filter thumbnails and in-dialog preview ([Filter Gallery overview](https://helpx.adobe.com/photoshop/desktop/effects-filters/get-started-with-filters/filter-gallery.html)). Each thumbnail is an original landscape drawing passed through that filter at its defaults; the preview area shows the same drawing through the visible stack, or through the filter being browsed when the stack is empty. The canvas preview and the one-undo OK are unchanged (`dialogs::effect_gallery::tests::gallery_shows_thumbnails_and_an_in_dialog_preview`). All 57 Photoshop-style filters are in the catalogue. Measured in
[effects-parity.md](effects-parity.md).
- **Done:** the Effect Gallery (#2883): Effect › Effect Gallery… browses the Artistic, Brush Strokes, Distort, Sketch, Stylize and Texture filters, edits each filter's settings, builds an ordered stack (add, reorder, hide, remove) with a live canvas preview, and OK applies it as separate appearance effects in one undo step (`dialogs::effect_gallery::tests::stack_ok_is_one_undo_step`, `stack_preview_is_one_interaction`).
+9 -1
View File
@@ -18,6 +18,13 @@ its current location. Opening the controls does not add a document undo step.
| Create | Fills / Strokes | Creates filled areas, stroked lines, or both. |
| Ignore Color | Color picker | Leaves out the selected color (white by default). |
The built-in presets other than Auto Color and Flat Logo all use Paths 50, and so do their enhanced
versions. Among those presets, Corners is 100 in Technical Drawing, 75 in Default, Black and White
Logo, Line Art and Outline, and 50 in the other eight. Noise is 1 px in Technical Drawing, 5 in High Fidelity Photo, 10 in Low Fidelity Photo, 15 in 3 Colors,
6 Colors, 16 Colors and Shades of Gray, 20 in Sketched Art, Line Art and Outline, 25 in Default and
Black and White Logo, and 90 in Silhouettes (see
[image-trace-quality.md](image-trace-quality.md#preset-paths-corners-and-noise)).
Sliders apply on release, and their numeric fields can be edited directly. Each committed
adjustment re-traces the selected object as one undo step and changes the preset to Custom.
While dragging, the preset and Enhanced row stay in place until release so the
@@ -142,7 +149,8 @@ further: the factor doubles until at most 150,000 remain or the working copy has
most 16,384 pixels (at most six times). A trace that would
make more than 8,000,000 anchor points (2,000,000 on the web) is refused to protect memory, with advice:
raise Noise, lower Paths or Colors, or pick a lower-fidelity preset. A trace keeps
about 90 bytes an anchor; a 4000 × 3000 photo at High Fidelity Photo makes about 1.9 million.
about 90 bytes an anchor; a 4000 × 3000 photo at Limited 64 colors, Paths 90, Corners 25 and Noise 4
makes about 1.9 million.
**Tracing in the background.** On the desktop a trace runs on a thread of its own, so the window
keeps answering while a photo or a large image is traced (#3098). Make, Make and Expand, the task
bar's Image Trace and the panel's Trace and presets start it. The status bar says "Tracing the
+44
View File
@@ -82,6 +82,9 @@ gradient, texture, sensor noise):
(The last two rows were measured without a limit; they are refused now.) The 4000 × 3000 High Fidelity
Photo trace was 86,000 anchors under the old limit; a noisier scan of the same size would have been
refused. At 8,000,000 anchors a trace keeps about 0.7 GB, with a peak of about 0.9 GB while tracing.
The preset rows were measured with the earlier preset values: High Fidelity Photo at Paths 90, Corners 25
and Noise 4, Low Fidelity Photo at Paths 60, Noise 12 and 16 Colors at Paths 70, Corners 55 and Noise 10
(see [Preset Paths, Corners and Noise](#preset-paths-corners-and-noise)).
## Collapsed loops keep few segments
A small closed loop (or a piece that returns to its own junction) whose fit collapsed to a single
cubic cannot stay as one, so it kept every positioned point as a straight segment. A looser Paths
@@ -225,6 +228,47 @@ in a smooth area), `a_staircase_that_runs_on_from_a_smooth_area_into_an_edge_kee
`a_staircase_of_mixed_one_pixel_and_longer_steps_is_one_slope_without_a_notch`,
`a_run_of_two_steps_keeps_its_corners`, `a_junction_on_a_step_ends_the_staircase_there` and
`a_step_whose_cracks_read_as_an_edge_ends_the_staircase_there`.
## Preset Paths, Corners and Noise
Every built-in preset other than Auto Color and Flat Logo traces at Paths 50. Corners and Noise are:
| preset | Paths | Corners | Noise | before |
|:--|--:|--:|--:|:--|
| Default | 50 | 75 | 25 | unchanged |
| High Fidelity Photo | 50 | 50 | 5 | 90, 25, 4 |
| Low Fidelity Photo | 50 | 50 | 10 | 60, 50, 12 |
| 3 Colors | 50 | 50 | 15 | 60, 60, 20 |
| 6 Colors | 50 | 50 | 15 | 65, 60, 16 |
| 16 Colors | 50 | 50 | 15 | 70, 55, 10 |
| Shades of Gray | 50 | 50 | 15 | 60, 50, 10 |
| Black and White Logo | 50 | 75 | 25 | 95, 80, 8 |
| Sketched Art | 50 | 50 | 20 | 50, 50, 100 |
| Silhouettes | 50 | 50 | 90 | 40, 60, 30 |
| Line Art | 50 | 75 | 20 | 80, 70, 10 |
| Technical Drawing | 50 | 100 | 1 | 95, 90, 4 |
| Outline | 50 | 75 | 20 | 80, 70, 10 |
The palettes, color counts, thresholds and other options of the presets did not change. The enhanced
versions of these presets use the same Paths, Corners and Noise.
Measured with the harness below (section 1, each preset on the inputs it is meant for), before and after:
anchors fall in 10 of the 12 changed presets, for example 5,494 to 3,136 (High Fidelity Photo), 33 to 27
(16 Colors), 27 to 14 (Black and White Logo) and 43 to 28 (Line Art); Silhouettes rises from 13 to 15 and
Sketched Art does not change. Outlines on flat art lie further from the true edge: off mean 0.100 to 0.123
px (16 Colors), 0.101 to 0.119 (6 Colors), 0.104 to 0.129 (3 Colors), 0.064 to 0.092 (Black and White Logo)
and 0.056 to 0.074 (Line Art), and off max 1.00 to 1.16 px in 6 Colors and 16 Colors. Technical Drawing
leaves 1.4 specks (was 0.5), and Outline's stroked center lines lie 0.095 px off (was 0.086) with 2.7
anchors a stroke (was 3.0). ΔE changes by 0.01 or less except Line Art and Outline (2.62 and 2.61 to 2.66
and 2.67); edge ΔE rises in 8 of the 12, for example 4.79 to 5.25 (16 Colors), 3.98 to 4.46 (3 Colors) and 9.46 to 10.03
(Black and White Logo). On synthetic shapes and photos of 300 to 4,000 px traced outside this harness,
High Fidelity Photo makes 41% fewer anchors and 16 Colors 20% fewer; the mean pixel difference of each
trace changes by −0.23 to +0.26 of a level; 16 Colors leaves 41 staircases of steps of 2 px or more where it left 40, High
Fidelity Photo 440 where it left 391, Low Fidelity Photo 96 where it left 77, Line Art 17 where it left 13, Outline 13
where it left 10 and 3 Colors 14 where it left 12. On the same images Sketched Art makes 4,084 anchors where it made
3,936 and Silhouettes 1,608 where it made 1,578. The figures for these twelve presets and their enhanced versions in
the other sections of this file, and in the Image Trace entries of
[target-app-parity.md](target-app-parity.md), were measured at the earlier values.
Test: `presets_have_their_paths_corners_and_noise` in `crates/trace/src/tests.rs`.
## A simpler contour walk
`contour::trace_mask` follows each region's outline along the pixel cracks with the inside on the
right. It used to build a table of outgoing directions for every pixel corner in a raster pass and
+15 -2
View File
@@ -32,6 +32,13 @@ every clause such as "all" or "what follows it". Type as a text object is टा
in `crates/ui-egui/src/i18n/tests.rs` checks the save, swatch, crop, resolution and path terms
and the point unit in every row.
Translate a label by its sense in the interface, not by its first dictionary sense. A ruler is
रूलर, never शासक (a sovereign); a text character is कैरेक्टर, never चरित्र (a personality); a
vector point is पॉइंट and a printer's mark is मार्क, never अंक (a digit); fills are फ़िल्स, never
भरता (the verb). In a minimum, desired and maximum range, Desired is वांछित, never देसी (local),
and a range message keeps both comparisons: minimum ≤ desired ≤ maximum. The same test rejects
these senses, and others found in the catalog, in every row whose English names the word.
A variable is वेरिएबल (plural वेरिएबल्स) in every sense: a data-merge variable and the
variable library, a variable font, a variable stroke width and an environment variable. It is
never चर, परिवर्तनीय or वैरिएबल. Joining paths, anchor points, point type or the lines of a
@@ -61,7 +68,10 @@ checks every `@msg` row's lists.
| Artwork | आर्टवर्क |
| Path / Anchor point / Handle | पाथ / एंकर पॉइंट / हैंडल |
| File path on disk | फ़ाइल पाथ |
| Fill / Stroke | फ़िल / स्ट्रोक |
| Fill / Fills / Stroke | फ़िल / फ़िल्स / स्ट्रोक |
| Point (vector) / Printer's marks | पॉइंट / मार्क्स |
| Ruler / Ruler origin | रूलर / रूलर ओरिजिन |
| Minimum / Desired / Maximum | न्यूनतम / वांछित / अधिकतम |
| Gradient / Mesh / Blend | ग्रेडिएंट / मेश / ब्लेंड |
| Pattern / Swatch | पैटर्न / स्वॉच |
| Type (text object) / Crop / Resolution | टाइप / क्रॉप / रिज़ॉल्यूशन |
@@ -89,7 +99,10 @@ percentage of the application's entire interface.
On 2026-11-13 the catalog has 8,782 rows, including 3,014 `@msg`, 47 `@msgplural` and 41
`@plural` rows. A wording pass on that date corrected 193 rows outside the plug-in
messages for meaning, dropped clauses, English left in place and the save, swatch, crop,
resolution, path and type terms. It is not a native proofread.
resolution, path and type terms. A second pass on that date corrected 85 short labels and
messages that used a word's unrelated sense, such as देसी (local) for Desired, शासक (a
sovereign) for a ruler and उच्च न्यायालय (a high court) for High Contrast. It is not a native
proofread.
The comparison covers release `v0.21.1` (`f6aa4309`) and current `main` (`3c7d9e1f`). Their
non-English catalogs have no missing Hindi keys except the names deliberately kept in English.
+5 -5
View File
@@ -15,7 +15,7 @@ translation never fails a test or a build: the interface shows the English text
`cargo xtask ci` prints the untranslated report (`cargo xtask i18n-report` alone), which lists per language the
strings missing from each catalog with the `complete_menus` flag and the messages missing from each language in
the `COMPLETE_MESSAGES` list. Every non-English language marked "full" has the flag, and all of them except
Bosnian, Bulgarian, Danish and Indonesian are in the list. No language has help content: there is no translated (or
Bosnian, Bulgarian and Indonesian are in the list. No language has help content: there is no translated (or
English) in-app help, and the docs are English.
**Native review:** every catalog was written clean-room from the English meaning, most of it by agents
@@ -31,13 +31,13 @@ Chinese by `imachus`). Georgian and the original Gujarati catalog are recorded a
| English | `en` | source, 100% | all | Latin | — | full | — |
| Simplified Chinese (Mandarin) | `zh-hans` | 5,102 rows, 791 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | all (3,554 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists 55 missing messages for it, which open #7116, #7211, #7330, #7378 and #7405 translate) | CJK UI font from craft-fonts (BIZ UDPGothic) when embedded, else a system font; IME with marked text at the caret; vertical type partial ([type-parity.md](type-parity.md)) | no | full | — (review) |
| Spanish | `es` | 4,009 / 4,020 (99.7%) | all (604 messages) | Latin | no | full | — (review) |
| Hindi | `hi` | 8,782 rows (2026-11-13): 3,014 `@msg`, 47 `@msgplural`, 41 `@plural`; the untranslated report on main `f793044f83` (2026-11-14) lists 676 missing rows for it: 608 in its own section and 68 that every checked language lacks | menus, dialogs, tooltips and scanned messages; contributor-approved loanword terminology; agent wording pass | UI and document text shape with HarfRust; installed desktop Devanagari fallback, or a `Deva` craft-fonts face; web font still needed | contributor terminology review; 2026-11-13 wording pass on messages and longer labels, with a glossary-term test; wider proofread welcome | complete catalog, machine-assisted draft | reported missing rows, native review, layout and web font |
| Hindi | `hi` | 8,782 rows (2026-11-13): 3,014 `@msg`, 47 `@msgplural`, 41 `@plural`; the untranslated report on main `f793044f83` (2026-11-14) lists 676 missing rows for it: 608 in its own section and 68 that every checked language lacks | menus, dialogs, tooltips and scanned messages; contributor-approved loanword terminology; agent wording pass | UI and document text shape with HarfRust; installed desktop Devanagari fallback, or a `Deva` craft-fonts face; web font still needed | contributor terminology review; 2026-11-13 wording pass on messages and longer labels, and a word-sense pass on short labels, with a glossary-term test; wider proofread welcome | complete catalog, machine-assisted draft | reported missing rows, native review, layout and web font |
| Gujarati | `gu` | 4,673 rows (2026-11-14): original 4,286 unchanged; all 387 additions contributor-reviewed and approved | menus and dialogs; status/error messages partial | installed Gujarati fonts on desktop; optional `Gujr` craft-fonts fallback; no Gujarati face in the pinned CI/release fonts or web startup list | original catalog reviewed by a native speaker; all 387 later additions approved by the contributor, with 11 wording corrections | contributor-reviewed catalog | remaining messages and a bundled Gujarati web font |
| Arabic | `ar` | 0 | none | document text: bidi layout and Paragraph Direction; interface: no right-to-left layout, no Arabic shaping in egui | no | none | 25–40 |
| French | `fr` | 9,166 rows, 3,436 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 178 missing messages for it | all | Latin | no | full | — (review) |
| Portuguese | `pt-br` | 9,378 rows, 3,563 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 55 missing messages for it, which open #7116, #7211, #7330, #7378 and #7405 translate; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) |
| Indonesian | `id` | 6,825 rows, 1,036 of them `@msg` and 47 `@msgplural` (counted 2026-11-14 on `main` at `676f0524cc`): the untranslated report lists 60 missing menu strings, 35 `tl!` literals and 13 undo labels for it; not in `COMPLETE_MESSAGES`, and checked against the same scanned messages it lacks 2,508; one plural form | most dialogs and tooltips; status and error messages partial | Latin | no | full | — (review) |
| Japanese | `ja` | 4,004 / 4,020 (99.6%) | all | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) |
| Japanese | `ja` | 4,004 / 4,020 (99.6%) | partial (3,562 `@msg` and 49 `@msgplural` rows, counted 2026-11-14: the untranslated report lists 98 messages without a Japanese row, 77 of which every complete-message language lacks; open #7116 and #7211 translate 35 of them) | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) |
| German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (3,563 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists 52 missing messages for it, which open #7116, #7211 and #7378 translate) | Latin | contributor-written, not reviewed | full | — (review) |
| Korean | `ko` | 4,870 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | all (3,438 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists 39 missing messages for it) | Hangul from an installed system font on the desktop (Apple SD Gothic Neo, Malgun Gothic, Noto Sans CJK KR…); the web build has no Korean UI font yet (craft-fonts); IME path exists | no (AI-drafted) | full | — (review; web font) |
| Vietnamese | `vi` | 4,886 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form; 4,046 plain + 18 `@plural` + 28 contextual + 794 `@msg` | dialogs and tooltips all; messages partial (1,750 `@msg` and 49 `@msgplural` rows, counted 2026-11-14: the untranslated report lists 1,875 messages without a Vietnamese row, 53 of which open #7211, #7378, #7405 and #7434 translate) | Latin with Vietnamese diacritics (stacked marks attach in precomposed, decomposed and keyboard forms, #4491; glyph tests pass) | contributor is a Vietnamese speaker; AI-assisted draft, further native review welcome | full | — (review) |
@@ -55,7 +55,7 @@ plus review. Native-speaker review is human work and isn't in the hours.
| Traditional Chinese (Taiwan) | `zh-hant` | 8,820 rows, 3,438 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 40 missing messages for it; menus, `tl!` literals and messages were complete when counted on 2026-10-21 (80% overall) | partial |
| Italian | `it` | 8,924 rows, 3,560 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 54 missing messages for it, which open #7116, #7211, #7378 and #7405 translate | full |
| Dutch | `nl` | 3,447 `@msg` and 48 `@msgplural` rows (counted 2026-11-13): in `COMPLETE_MESSAGES`, and the untranslated report lists 41 missing messages for it; one/other plurals | full |
| Danish | `da` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one/other plurals | full |
| Danish | `da` | 7,422 rows, 1,636 of them `@msg` and 48 `@msgplural` (counted 2026-11-14 on `main` at `edb228ac3`): in `COMPLETE_MESSAGES`, and the untranslated report lists the same missing messages for it as for Greek, Spanish and Finnish; one/other plurals | full |
| Russian | `ru` | 4,009 / 4,020 | full |
| Ukrainian | `uk` | 8,802 rows, 3,439 of them `@msg` and 48 `@msgplural` (counted 2026-11-13); one/few/many plurals | full |
| Hungarian | `hu` | 9,166 rows, 3,444 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 40 missing messages for it, and lists 47 menu strings, 25 `tl!` strings, 11 undo labels and 45 swatch names without a row; one plural form | full |
@@ -67,7 +67,7 @@ plus review. Native-speaker review is human work and isn't in the hours.
| Bulgarian | `bg` | 5,245 rows, 763 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial | full; AI-drafted, native review pending |
| Greek | `el` | 5,251 rows, 795 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | full; native review welcome |
| Georgian | `ka` | 5,200 rows (counted 2026-10-21): menus, panels, dialogs, plurals (one count-neutral form) and messages, tested complete; reviewed by a native speaker | full |
| Turkish | `tr` | 5,558 rows, 880 of them `@msg` (counted 2026-10-28); 6 menu strings and the default swatch names still fall back to English | full |
| Turkish | `tr` | 9,175 rows, 3,446 of them `@msg` and 48 `@msgplural` (counted 2026-11-13); 41 messages, 62 menu strings, 26 `tl!` literals, 13 undo labels and the default swatch names still fall back to English | full |
| Catalan | `ca` | 5,626 rows, 881 of them `@msg` (counted 2026-10-28): menus, `tl!` literals and messages complete when counted; one/other plurals; every `ca-*` locale resolves here | full; AI-drafted, native review welcome |
Twenty-seven non-English catalogs in all (registry count, 2026-11-06). Illustrator, as its public documentation lists, ships around two dozen
+373 -71
View File
@@ -1,6 +1,6 @@
# VectorCraft MCP server
> **Last updated:** 2026-11-14 · **Change:** Pencil continuation, straight-line and closing cursors; scores and estimates unchanged; no response-shape change. Previously: Snap to Point with Smart Guides off draws no Smart Guides labels or lines, #7250; no response-shape change. Previously: Envelopes: a top object's side whose pieces are all one curve split at anchors follows that curve's parameter (#7097); existing response shapes retained.
> **Last updated:** 2026-11-14 · **Change:** Pencil ignores Smart Guides and Snap to Point and shows no Smart Guides, #7252; no response-shape change. Previously: Pencil continuation, straight-line and closing cursors; scores and estimates unchanged; no response-shape change. Previously: Snap to Point with Smart Guides off draws no Smart Guides labels or lines, #7250; no response-shape change. Previously: Envelopes: a top object's side whose pieces are all one curve split at anchors follows that curve's parameter (#7097); existing response shapes retained.
`vectorcraft-cli mcp` runs a [Model Context Protocol](https://modelcontextprotocol.io) server on stdio
(newline-delimited JSON-RPC 2.0; see [Protocol](#protocol) for version negotiation). Agents use it to
@@ -647,8 +647,8 @@ sampler.
| `inspect_document` | `{depth?, childLimit?, ids?, kind?, limit?, offset?}` | Artboards (with `color`, `#rrggbb`, for one with a background color), layer tree (ids, kinds, bounds, paint), selection, history, tool. A type node's `fill`, `stroke` and `strokeWidth` are the paint its characters show (its first run's); fills and strokes of the type object itself come as `objectFill`, `objectStroke` and `objectStrokeWidth`. Paint comes as a label (`"#ff0000"`, `"None"`, `"G1 40% (#ff9999)"` for a swatch at a tint); `paint.proxies` gives it as an object. |
| `inspect_ui` | `{}` | UI state. Remote mode only. |
| `select_tool` | `{tool}` | `selection`, `directSelection`, `pen`, `rectangle`, `ellipse`, `polygon`, `star`, `lineSegment`, … |
| `pointer_gesture` | `{events:[{kind,x,y,mods?,backquote?,pressure?,tiltX?,tiltY?,rotation?,holdMs?}], tool?, mods?, backquote?}` | `kind` is one of `down`, `drag`, `up`, `move`, `doubleclick`. `backquote` is the held state for each event; the top-level value supplies its default, and a `backquote` that is not a boolean is an error. Events go through the same path as the mouse. Headless, a `move` replies after the tool's highlight under the pointer is ready, including the Shape Builder's and the Live Paint Bucket's regions of a large selection, which the desktop app finds off the UI thread and highlights after the reply. The release of a Shape Builder click or drag over such a selection replies once the merge is applied, headless and in the desktop app, which computes the merge off the UI thread. In the desktop app, a gesture sent before then is made after the merge, and its reply comes once it is made; a command that can edit, select, save or redraw the documents (a preference, the proof and separations views), sent before then, waits for the merge and runs after it. |
| `pointer_gesture` | `{events:[{kind,x,y,mods?,backquote?,pressure?,tiltX?,tiltY?,rotation?,holdMs?}], tool?, mods?, backquote?}` | `kind` is one of `down`, `drag`, `up`, `move`, `doubleclick`. `backquote` is the held state for each event; the top-level value supplies its default. Events go through the same path as the mouse. Headless, a `move` replies after the tool's highlight under the pointer is ready, including the Shape Builder's and the Live Paint Bucket's regions of a large selection, which the desktop app finds off the UI thread and highlights after the reply. Remote, the events wait until the keys and other input sent before them (`press_key`, `type_text`) have reached the app, so a key pressed between two calls of one drag applies before the release. When that input is dropped because the document, a dialog or the focus changed first, the call fails and its events are not sent. The release of a Shape Builder click or drag over such a selection replies once the merge is applied, headless and in the desktop app, which computes the merge off the UI thread. In the desktop app, a gesture sent before then is made after the merge, and its reply comes once it is made; a command that can edit, select, save or redraw the documents (a preference, the proof and separations views), sent before then, waits for the merge and runs after it. |
| `pointer_gesture` | `{events:[{kind,x,y,mods?,backquote?,pressure?,tiltX?,tiltY?,rotation?,holdMs?}], tool?, mods?, backquote?}` | `kind` is one of `down`, `drag`, `up`, `move`, `doubleclick`, `tripleclick` (`doubleclick` follows the second click's `up`, and `tripleclick` the third's). `backquote` is the held state for each event; the top-level value supplies its default, and a `backquote` that is not a boolean is an error. Events go through the same path as the mouse. Headless, a `move` replies after the tool's highlight under the pointer is ready, including the Shape Builder's and the Live Paint Bucket's regions of a large selection, which the desktop app finds off the UI thread and highlights after the reply. The release of a Shape Builder click or drag over such a selection replies once the merge is applied, headless and in the desktop app, which computes the merge off the UI thread. In the desktop app, a gesture sent before then is made after the merge, and its reply comes once it is made; a command that can edit, select, save or redraw the documents (a preference, the proof and separations views), sent before then, waits for the merge and runs after it. |
| `pointer_gesture` | `{events:[{kind,x,y,mods?,backquote?,pressure?,tiltX?,tiltY?,rotation?,holdMs?}], tool?, mods?, backquote?}` | `kind` is one of `down`, `drag`, `up`, `move`, `doubleclick`, `tripleclick` (`doubleclick` follows the second click's `up`, and `tripleclick` the third's). `backquote` is the held state for each event; the top-level value supplies its default. Events go through the same path as the mouse. Headless, a `move` replies after the tool's highlight under the pointer is ready, including the Shape Builder's and the Live Paint Bucket's regions of a large selection, which the desktop app finds off the UI thread and highlights after the reply. Remote, the events wait until the keys and other input sent before them (`press_key`, `type_text`) have reached the app, so a key pressed between two calls of one drag applies before the release. When that input is dropped because the document, a dialog or the focus changed first, the call fails and its events are not sent. The release of a Shape Builder click or drag over such a selection replies once the merge is applied, headless and in the desktop app, which computes the merge off the UI thread. In the desktop app, a gesture sent before then is made after the merge, and its reply comes once it is made; a command that can edit, select, save or redraw the documents (a preference, the proof and separations views), sent before then, waits for the merge and runs after it. |
| `draw_path` | `{points \| d, closed?, fill?, stroke?, strokeWidth?, dash?, cap?, join?, opacity?, name?, effects?}` | `points` is `[[x,y],…]` or `[{x,y,in?,out?,smooth?},…]`. `d` is SVG path data. The options are below. |
| `draw_shape` | `{shape, …geometry, fill?, stroke?, strokeWidth?, dash?, cap?, join?, opacity?, name?, effects?}` | `rectangle`/`ellipse`: `x,y,width,height` (plus `radius` for corners). `polygon`: `cx,cy,radius,sides`. `star`: `cx,cy,radius1,radius2,points`. Polygons and stars take `rotation` in degrees clockwise (0: a corner or point straight up). `line`: `x1,y1,x2,y2`. |
| `set_paint` | `{fill?, stroke?, strokeWidth?, ids?}` | Applies to the selection (or `ids`) and becomes the default for new art. Returns `{applied, objects}`: `objects` is the number of objects painted, as the `objects` of the `paint.setFill`, `paint.setStroke` and `stroke.set` replies (the largest of them): a selected group counts the objects inside it. When it is 0, a `note` says that only the paint for new art changed. |
@@ -666,7 +666,7 @@ sampler.
| `pathfinder` | `{operation, ids?}` | `unite`, `minusFront`, `intersect`, `exclude`, `divide`, `trim`, `merge`, `crop`, `outline`, `minusBack`. For a live version, apply the `pathfinder.*` effect to a group (`effect.apply` groups several loose selected objects first). In remote desktop mode, `run_command` and `invoke_menu` can both call `pathfinder.repeat` with `{}`. Successful panel operations, direct `object.pathfinder.*` commands, and `command.batch` steps (including Actions panel playback) set its history. Repeat applies it to the current selection using current Pathfinder Options. Trap, compound-shape edits, live effects and `action.batch` do not set history. With no history it returns "no Pathfinder operation applied yet". |
| `transform` | `{ids?, dx?, dy?, rotate?, scale?, scaleX?, scaleY?, reflect?, shear?, origin?, copy?}` | Runs move, rotate, scale, reflect, shear in that order. `rotate` is in degrees counter-clockwise; a positive `shear` moves the top to the right. With `copy`, the first step duplicates. |
| `create_graph` | `{type?, x, y, width, height, csv? \| series?, categories?, rows?}` | The nine Illustrator graph types: column, stacked column, bar, stacked bar, line, area, scatter, pie and radar. An empty CSV cell or a `null` in `rows` is a blank value; a number in straight quotes is a label. Edit later with `graph.setData` / `graph.setType` via `run_command`. `graph.column {design, columnType: vertical\|uniform\|repeating\|sliding, each, fractions: chop\|scale}` draws the columns or bars of the series picked with Group Selection (or `seriesIndexes`, or all of them) with a graph design saved by `graph.design`; with no `design` it returns the series' current design and options. Type in the design that holds a `%` followed by two digits shows each column's value, with at least the first digit's places before the decimal point and the second digit's places after it. |
| `text_wrap` | `{ids?, offset?, invert?, release?}` | Area type below the objects (same layer) flows around them. |
| `text_wrap` | `{ids?, offset?, invert?, release?}` | Area type below the objects (same layer) flows around them. `offset` is in points, from -1000 to 1000 (default 6). An offset outside that range or not a number, or an `invert` or `release` that is not true or false, is an error and changes nothing; `object.textWrap.make` and `object.textWrap.options` refuse the same values. |
| `undo` / `redo` | `{}` | Many calls that change nothing record no undo step, for example `object.align` with nothing to move, `object.move` by 0 without `copy` and `object.showAll` with nothing hidden. `undo` (`edit.undo`) after such a call undoes the step before it. `inspect_document` lists the undo steps as `history`. `run_command` with `edit.clearHistory` removes every undo and redo step of the active document and returns `{cleared}`, the number of steps removed; the document stays as it is, and the command records no undo step. `run_command` with `edit.newDocumentFromState` and `{index?, created?}` makes a new document holding the art as it was at that state and makes it the active document, and returns `{index, title}`. `index` is a row of the History panel, with 0 the oldest state kept, and an omitted or null `index` uses the current state. An `index` that names no state fails with `no history state N`, one that is not a whole number with `` `index` must be a whole number ``, and either changes nothing. `created` is File Info's created date in Unix seconds or null, and defaults to now. The new document takes the next `Untitled-N` name, is marked unsaved, has nothing selected and has no steps to undo or redo. It is out of symbol, opacity-mask and pattern editing, and the art drawn in a pattern's tile is stored in the pattern, as `object.pattern.done` stores it. The source document and its steps stay as they are, except that typing or a drag in progress is first kept as a step. When a command keeps a drag whose preview Real-time Drawing off had not applied yet (`edit.undo`, `edit.redo`, `edit.clearHistory`, `edit.newDocumentFromState` and the other commands that end a drag first), and applying that preview gives a warning, the command's result carries it as `previewWarning`, and as `warning` too when the command gives no warning of its own; the app's status bar shows `warning`. While the Type tool edits type, `undo` and `redo` put its caret, with no characters selected, at the end of the text the step changed: after the text it puts back or retypes, or where the text it takes out was. A step that changes no characters, such as a formatting change, leaves the caret and the selection where they are. |
For example, create editable Japanese area type with the engine's vertical writing mode:
@@ -813,10 +813,18 @@ look was edited keep their look. Read as a source, by `graphicStyle.new`, `graph
style link of its own gives the look and the style link of the topmost path directly in it, and a move with
`appearance.transfer` clears each of those paths; a series group that has one of them is read itself.
`transparency.set` and `object.setProps` with an opacity, blend mode, isolation, knockout or knockout shape for the
object, given a series group's id or the selected series group, set them on the group itself, and the graph stores them
for that series. The series group has them again when the graph is drawn again, and they apply to the group as a whole
in addition to each path's own opacity and blend mode. `addon.arrange.randomizeOpacity` and `addon.arrange.sort` with
`apply: "opacity"` given a series group, and Global Edit when a series group takes an edit from another match, store
what they change on the group the same way. Each of the commands above that clears the group's own opacity, blend mode, isolation
and knockout also removes what the graph stores for it.
The other commands given a series group's id edit the group itself, and the graph does not store what they set on it:
`transparency.set` and `object.setProps` with an opacity or blend mode for the object, the `effect.*` commands and
`appearance.addEffect`, and the paint, stroke and gradient commands with a fill or stroke `item`, which is a row of the
group's own stack. The Appearance panel and `appearance.setActiveItem` read the selected group's own rows.
the `effect.*` commands and `appearance.addEffect`, and the paint, stroke and gradient commands with a fill or stroke
`item`, which is a row of the group's own stack. The Appearance panel and `appearance.setActiveItem` read the selected
group's own rows.
When a graph is drawn again (a change to its data, its type, or its column or marker designs), it draws the art of a
column or marker design in a series again from the design, and an edit made to that art is lost. The art is any object
@@ -887,6 +895,17 @@ kind takes them when the graph is drawn again. The graph saves the paragraph att
`categoriesPara` and `legendPara` in its label styles, and still sets each label's alignment for its place. The legend
leaves room for a legend label's stored indents, spacing and leading.
When a graph is drawn again, its plot moves and scales with any move or scale of the graph made since it was last
drawn. The graph saves the bounds of its art other than type as `placedArt` (and the bounds of all of its art as
`placed`), and measures the move or scale on those. Changing the size or indents of the labels, moving a label on its
own, `type.createOutlines` on labels and `object.path.outlineStroke` on the graph's strokes do not move or scale the
plot. Outlined labels are no longer type: moving one of them on its own, like moving a swatch, an axis or a tick mark on
its own, can move or scale the plot at the next redraw. When all of a graph's art other than type has been deleted, or
the art measured has no width or no height, a redraw leaves the plot where it is and does not follow a move or scale of
the graph. A graph opened from a `.ai` file
saves `placedArt` too. A graph saved without `placedArt`, or with a `placedArt` that has no width or height, is
measured on `placed`.
The `appearance.*` commands, `graphicStyle.apply` and `graphicStyle.redefine`, and the paint, stroke, gradient and
transparency commands given an `item` (or the Appearance panel's active row) store a graph label's object rows for its
kind of label: the paint, weight, opacity and blend mode of its topmost visible fill and stroke row, their order and
@@ -1110,8 +1129,9 @@ way. `document.exportPdf {useArtboards: false}` writes that one page as well, an
art's bounds is measured without what the plot leaves out: non-printing layers, objects at 0% opacity, placed images,
gradient meshes whose every point is at 0% opacity, and paths without effects whose fills and strokes paint nothing.
An image or a mesh with a Crop Marks effect, or with a Vector Halftone effect that makes dots, is measured.
Such objects in the art of a symbol that is not dynamic are left out of its instances too, and an instance whose
symbol holds nothing else is left out.
Such objects in the art of a symbol are left out of its instances too, and an instance whose symbol holds nothing
else is left out. In the art of a dynamic symbol, an object that an instance's fill, stroke or opacity makes
the plot draw is measured.
A document whose only visible art is left out is refused, and Export As refuses an HP-GL plot of it before the save
dialog. A PDF of that page, from either command, carries no Preserve Editing data; when Preserve Editing is on (as it
is in VectorCraft Default), the export comes back with a warning that says so. A `.ai` export with
@@ -1126,8 +1146,9 @@ follows as after Export As: Save PDF without its Artboards section, SVG Options
options, EPS Options, DXF Options without Export Selected Art Only, Text Export Options with Selection Only on, or an
exporter plug-in's parameters. Other formats are written at once. OK writes the selection alone, cropped to it, as
`document.exportSelection` writes it, with the chosen options. While a dialog is open the call returns `null`, and
OK returns what `file.exportAs` returns, which has no `format` or `bounds` (Text Export Options: `{path}`). To
write without a dialog, pass `path` or any option. `file.exportAs {path, exportSelection: true, …options}` writes the selection the same way.
OK returns what `file.exportAs` returns, which has no `format` or `bounds` (Text Export Options: `{path}`). Any
option skips the options dialog, but without `path` the save panel still picks the file. To write without a
dialog, pass `path`. `file.exportAs {path, exportSelection: true, …options}` writes the selection the same way.
Raster exports default to the document's raster effects resolution. An explicit `scale` sets pixels per
point (72 times that value in ppi); `options.ppi` wins over either scale option. CLI `run --export` and
@@ -1276,6 +1297,15 @@ empty `boxes`, and `thumbnail: 1` shows what that page prints. A page whose `Use
opens at its size in points, the file's numbers times the `UserUnit` (one above 75,000 counts as 75,000), and
`document.pdfInfo` gives its size and boxes in points too. A `.ai` whose editing data has its artboards in the units of
such a page opens from that data at the page's size.
`linkPages: true` (File › Open's Import PDF dialog: Import PDF pages as links) opens each page `pages` picks as a
placed document linked to that page of the file, as `file.place` with `link` places one, instead of as editable art.
Each page gets an artboard of its `cropTo` box (`bounding`: its art's bounds), in a row from the left 36 pt apart, and
a layer named after the page (`Page 2`). The document opens in CMYK when most of those pages are painted mostly in
CMYK, unless `colorMode` names a mode. All the pages' objects share one copy of the file. The Links panel lists each
page, and `links.embed` makes a page editable art.
`document.open` refuses `linkPages` for a file that is not a PDF or a PDF-compatible `.ai`, for a file given as
`dataBase64` without a path, with a `password`, and with `textAs: "outlines"`, and rejects a `linkPages` that is not
true or false. Save then asks for a name.
`document.open`, `file.place` and `document.pdfInfo` reject a `cropTo`, `crop`, `password`, `colorMode` or `textAs`
that is not a string, and `document.pdfInfo` rejects a `thumbnailSize` that is not a number. The error names the param,
and nothing opens or is placed. Leaving a param out or giving null keeps its default.
@@ -1291,6 +1321,7 @@ CMYK too.
```json
{"name":"run_command","arguments":{"command":"document.pdfInfo","params":{"path":"/tmp/brochure.pdf","thumbnail":2,"cropTo":"trim"}}}
{"name":"run_command","arguments":{"command":"document.open","params":{"path":"/tmp/brochure.pdf","pages":"2-3","cropTo":"trim"}}}
{"name":"run_command","arguments":{"command":"document.open","params":{"path":"/tmp/brochure.pdf","linkPages":true}}}
```
PostScript files (`.eps`, `.epsf`, `.ps`, and `.ai` files saved in older formats or without PDF compatibility) open through the EPS
@@ -1724,6 +1755,16 @@ one opens as before and draws it as `rgb`:
→ invalid parameters for `effect.setParams`: `channel` must be one of rgb, red, green, blue
```
Radial Blur's Blur Center, `centerX` and `centerY`, must each be a number from 0 to 1: the share of the object's bounds
across from its left edge and down from its top edge (0.5 each, the object's center, by default). The effect turns or
zooms about that point, which moves and scales with the object. `effect.apply` and `effect.setParams` refuse any other
value themselves in the same way, and a document saved with one draws it at the nearest edge:
```json
{"id": "effect.setParams", "params": {"index": 0, "params": {"centerX": 1.5}}}
→ invalid parameters for `effect.setParams`: `centerX` must be a number from 0 to 1
```
A number outside the range the effect documents is still stored as given and clamped when the effect draws. A
plug-in effect's parameters are resolved against its manifest instead. `effect.setParams` as a step of
`command.batch` is checked when the step runs, against the effects the steps before it added, and the batch then
@@ -3216,6 +3257,13 @@ already walks Image Trace. Short agent entry points:
unchanged. No undo step is added when the symbols are already in order. The panel menu's Thumbnail View, Small List
View and Large List View change only how the panel shows the symbols: thumbnails, or rows with a thumbnail and the
name (smaller rows in Small List View).
- Panel views: `ui.panelView {panel, view?}` sets the view of the Swatches, Graphic Styles or Symbols panel, as the
view items of its panel menu (and the Swatches panel's list and thumbnail buttons) do. `panel` is `swatches`,
`graphicStyles` or `symbols`. The Swatches panel takes `smallThumb`, `mediumThumb` (the default), `largeThumb`,
`smallList` or `largeList`; the Graphic Styles and Symbols panels take `thumbnail` (the default), `smallList` or
`largeList`. Without `view` the command answers the current one. Each panel keeps its own view for the session, and
the document does not change. An unknown panel or view is an error, and then nothing changes. It answers
`{panel, view}`.
- The Brushes panel menu's Select All Unused: `brush.unused` (a query) lists the brushes no stroke paints with, in
library order, and the panel selects them. Strokes in the art (opacity masks included), symbols, graph designs,
pattern tiles (with the tiles that pattern editing's Cancel puts back) and graphic styles count, and so do the
@@ -3332,32 +3380,48 @@ already walks Image Trace. Short agent entry points:
`dataset.import`, adds no undo step when it leaves the variables and data sets as they were. The library
is XML whose `variableSets` element holds `variableSet` elements, each with a `variables` element of `variable`
elements (`varName`, `trait`) and a `sampleDataSets` element of `sampleDataSet` elements (`dataSetName`). Every
data set is deleted, and every variable of the document is deleted with its bindings and values, except a text
variable the library also declares: it keeps its bindings, so selecting one of the library's data sets changes the
objects bound to it. Then the library's other variables whose `trait` is `textcontent` are defined as text
variables, unbound. In each data set, the element named
after a text variable gives its value: the text of its `p` elements, one paragraph each. An element without a `p`
element gives no value. Variables of other kinds are left out with their values: the reply lists them in `skipped`
(`name`, `trait`), and its `warning` names each `trait` with its count. The reply's `deleted` and `deletedDataSets`
data set is deleted, and every variable of the document is deleted with its bindings and values, except a variable
the library also declares with the same kind: it keeps its bindings, so selecting one of the library's data sets
changes the objects bound to it. Then the library's other variables are defined, unbound, with the kind their
`trait` gives: `textcontent` is text, `visibility` is visibility, `fileref` (a linked file) is image and
`graphdata` is graph. A variable without a `trait`, or with one VectorCraft doesn't know, is defined of unknown
kind and takes no values. In each data set, the element named after a variable gives its value:
- text: the text of its `p` elements, one paragraph each; an element without a `p` element gives no value;
- visibility: `true` or `false`, in any case;
- image: a file's absolute path (from `/`, `\\` or a drive's root) or a `file:` URL of one, whose `%XX` escapes
are decoded; the file isn't read, so a library can name files that aren't there;
- graph: a `data` element. Its optional `propertyRow` holds a corner cell and then the series names, and each `row`
of its `values` holds the row's category in a first `value` whose `key` is `name`, then a `value` per series. A
value that is empty, marked `type="string"` or not a number is a blank. The data is read as the CSV
`graph.setData` takes, at most 256 KiB of it. Graph data with more than 10,000 rows or 256 series, which a
graph doesn't hold, is refused, by `variable.saveLibrary` too. `variable.saveLibrary` also refuses graph data
with neither rows nor series, which loads back as no value.
Visibility, image and graph values are read without the white space round them, and an element holding only white
space gives no value. The reply's `variables` lists the library's variables, and `deleted` and `deletedDataSets`
list the variables and data sets the load deleted. While the variables are locked, a library that would delete or
define a variable fails with "variables are locked". The reply is `{variables, deleted, deletedDataSets, names,
skipped, warning?}`. A library larger than 8 MiB, one whose elements nest more than 256 deep, and one with more
define a variable fails with "variables are locked". The reply is `{variables, deleted, deletedDataSets, names}`. A
library larger than 8 MiB, one whose elements nest more than 256 deep, and one with more
than 256 variables or 1,000 data sets are refused. Its document type declaration may hold an external identifier,
comments, processing instructions and up to 64 entity declarations whose values are plain text (no `<`, `&` or
`%`). A declaration that holds anything else, and entity references that expand the library past 8 MiB, are
refused. So are a variable or data set declared twice and a value for a variable the library doesn't declare.
`variable.saveLibrary {path?}` (the panel menu's Save Variable Library…) writes the document's text variables and
every data set as a variable library in the layout `variable.loadLibrary` reads: a `variable` element with
`varName` and `trait="textcontent"` for each text variable, and a `sampleDataSet` element with `dataSetName` for
each data set, holding an element named after each text variable it has a value for, with one `p` element per line
of the value. Loading the file gives back the same text variables, data set names and text values. Variables of
other kinds are left out with their values: the reply lists them in `skipped` (`name`, `kind`), and its `warning`
names each kind with its count. Saving is refused for a document without a text variable, for a text variable whose
name isn't an XML name (it starts with a letter or `_` and holds only letters, digits, `-`, `_` and `.`), for a
data set whose name or text value holds a control character, for a data set name with white space at either end,
for a name longer than 255 characters or used twice, and past `variable.loadLibrary`'s caps (256 variables, 1,000
data sets, 8 MiB). Without `path` the reply is `{xml, variables, datasets, skipped, warning?}`;
with it, the file is written and the reply is `{path, variables, datasets, skipped, warning?}`.
refused. So are a variable or data set declared twice, a value for a variable the library doesn't declare, and a
value that doesn't fit its variable's kind.
`variable.saveLibrary {path?}` (the panel menu's Save Variable Library…) writes the document's variables and every
data set as a variable library in the layout `variable.loadLibrary` reads: a `variable` element with `varName` for
each variable, with the `trait` and `category` of its kind (`textcontent` and `&ns_flows;`, `visibility` and
`&ns_vars;`, `fileref` and `&ns_vars;`, `graphdata` and `&ns_graphs;`; neither for a variable of unknown kind), and
a `sampleDataSet` element with `dataSetName` for each data set, holding an element named after each variable it has
a value for. A text value is written with one `p` element per line, a visibility value as `true` or `false`, an
image value as its path (a path from the root of a Unix file system as a `file:///` URL), and a graph value as a
`data` element, with a blank category marked `type="string"`. Loading the file gives back the same variables with their kinds, data set names and values; graph
data comes back in the CSV form Graph Data writes. Saving is refused for a document without a variable, for a
variable whose name isn't an XML name (it starts with a letter or `_` and holds only letters, digits, `-`, `_` and
`.`; a variable of unknown kind has no values, so its name isn't checked), for a name or value that holds a control
character, for a name with white space at either end, for a name longer than 255 characters or used twice, for an
image value that isn't a file's absolute path, and past `variable.loadLibrary`'s caps (256 variables, 1,000 data
sets, 8 MiB). Without `path` the reply is `{xml, variables, datasets}`; with it, the file is written and the reply
is `{path, variables, datasets}`.
- Batch: `action.batch {commands, source?: folder|datasets, folder?, destination?: none|saveAndClose|folder,
destinationFolder?, overrideOpen?, overrideSave?, overrideExport?, includeSubdirs?, stopOnError?}` plays an
action on every file in a folder or every data set (the Actions panel menu's Batch…). Override Save and Override
@@ -3660,8 +3724,20 @@ already walks Image Trace. Short agent entry points:
`ui.imageTracePresetDialog {params?, rename?}` opens the Image Trace panel's Save as New Preset… or Rename… dialog.
- Live Paint: `livePaint.make`, `livePaint.merge`, `livePaint.fill {group?, point}`, `livePaint.strokeEdge`,
`livePaint.info {group?|id?}`, `livePaint.release`, `livePaint.expand`, and `livePaint.gapOptions {ids?, detect?, size?,
color?, closeGaps?}` (Object › Live Paint › Gap Options). Make gives each covered face the fill of its front-most
covering source, including compound paths with shared curved boundaries. Expand keeps those face fills.
color?, closeGaps?}` (Object › Live Paint › Gap Options). Make gives each covered face the fill rows that draw of
its front-most covering source that has one, including compound paths with shared curved boundaries, and each edge
the stroke rows that draw of its source. A row draws when it is shown and has paint and an opacity above 0; a
stroke also needs a weight above 0, and a source whose opacity is 0 has no rows that draw. The rows keep their order
and settings (opacity, blending mode, effects, overprint, and a stroke's weight, caps, joins, dashes and alignment),
gradients keep their fit on the source, and the face or edge takes the source's opacity and blending mode. Where a
path is cut into several edges, each edge runs the way the path does, its dashes go on from where it starts along
the path (Align Dashes to Corners is turned off on it), the arrowheads stay only at the path's own ends, and a width
profile or brush stays only on an edge that runs the whole path. A gradient applied along a stroke starts again on
each cut edge, and an inside or outside stroke is drawn centered on an edge that is an open piece of a closed path.
When `livePaint.gapOptions` or `livePaint.merge` cuts an edge into pieces, each piece takes the edge's paint, with
its rows fitted to its part of the edge in the same way. A face or edge given no such row has one unpainted row. Rows that do not draw stay with the stored sources, which
`livePaint.release` returns unchanged. The source's object effects and the strokes' pen pressure are not carried.
Expand keeps those face fills.
`livePaint.expand` keeps each face and edge that draws, with all its fill and stroke rows: it is shown, its opacity
is above 0, and a fill or stroke row is shown, has paint and an opacity above 0 (a stroke also a weight above 0),
whichever row is on top. A painted lower row under an unpainted top row keeps the part; a part whose only painted
@@ -3734,6 +3810,31 @@ already walks Image Trace. Short agent entry points:
`livePaint.strokeEdge` when an edge is within 4 screen pixels of the pointer and `livePaint.fill` otherwise, and
Shift-click runs `livePaint.fill`. With both off, a click runs nothing. With `highlight` off, the face or edge under
the pointer is not outlined.
A double-click runs the command a click there runs with `scope: "connected"`, and a triple-click with
`scope: "samePaint"`; Alt held runs nothing, and so does Cmd/Ctrl (`mods.cmd`), whose presses go to the selection
tool Cmd lends. With Space held (`mods.space`, the view tools), a press, a
double-click or a triple-click runs nothing, and a move outlines no face or edge. On a face, `connected` fills the face and every face reached from it
across edges that draw no stroke (an edge that is hidden or fully transparent, or whose strokes are each hidden,
without paint, 0 pt wide or fully transparent); a stroked edge, a hidden face, a gap
that Gap Detection closes and the group's outside stop it. `samePaint` fills every face
of the group, joined to it or not, whose fill is the `match` paint. On an edge, `connected` paints the edge and the
edges joined to it end to end that draw with the stroke paint `match`, and `samePaint` paints every edge of the
group that draws with that stroke paint; the stroke's weight is not compared. An edge draws with the paint of its
topmost stroke that is shown, with paint, above 0 pt and not fully transparent, and with none when the edge is fully
transparent or has no such stroke. Each click of a double-click or triple-click runs as it
arrives and is its own undo step. `match` is the paint the face or edge had before the first of the double-click's
two clicks or the triple-click's three that painted it, or before the double-click when the triple-click's double-click
reached it, so the clicks before the last do not change what it matches;
a first click that made the Live Paint group counts when the face it filled is the clicked face. Hidden faces and edges are not painted. An edge takes the
paint and weight in its topmost stroke that is shown, or in a new stroke above the others when it has none. These rules
for what faces and edges join and what paints match are VectorCraft's own. The help page describes the gestures:
https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/paint-with-the-live-paint-bucket-tool.html.
`livePaint.fill {scope}` and `livePaint.strokeEdge {scope}` take `scope` as `"one"` (the default), `"connected"` or
`"samePaint"`; any other value is an error and changes nothing. `match` is a paint in the model JSON that
`document.node {id}` writes for an appearance's paints (`{"type": "none"}` for none), taken only with a scope that compares paints, and
defaults to the face's fill or the stroke paint the edge draws with. `livePaint.fill` refuses a `scope` other than `"one"` with
`points`. Both replies add `count`, the number of faces or edges the click reaches, including those that already had the
paint.
With the Live Paint Bucket selected and no drag in progress, Left and Right without Shift, Alt or Cmd/Ctrl (which
keep their shortcuts) run `paint.setFill {swatch, ids: []}` (or
`paint.setStroke` when the Stroke proxy is in front) with the color swatch before or after the active paint in the
@@ -3750,7 +3851,8 @@ already walks Image Trace. Short agent entry points:
objects; otherwise it returns an error and does not merge the objects.
A Live Paint command that changes nothing records no undo step, keeps the redo steps, leaves a saved document saved
and replies as usual: `livePaint.fill` (a click, or a Bucket drag) when every face it reaches already has the paint,
`livePaint.strokeEdge` when the edge already has the paint and the width, `livePaint.gapOptions` (and the Gap Options
`livePaint.strokeEdge` when the topmost shown stroke of each edge it reaches already has the paint and the width,
`livePaint.gapOptions` (and the Gap Options
dialog's OK) when every group already has the options and `closeGaps` finds no gap, and `livePaint.make` or
`livePaint.merge` given one Live Paint group alone, which they select. Such a fill or stroke also leaves the recent
colors and the last color as they are. `livePaint.gapOptions` makes a group's faces again, with new ids, only when it adds a path or when Gap Detection is turned on or off or given a new size while it is on.
@@ -5127,8 +5229,12 @@ when its symbol or a symbol nested in its art holds an instance of itself, and w
nest 32 deep in its art. PDF and EPS export and the SVG of an artboard leave out art outside the page or artboard, and
Skip Blank Artboards skips an artboard with no art on it. For both, the box of an instance whose art holds brushed
strokes grows by 16 times the widest stroke (at least 1 pt) of the objects in its art that have a brushed stroke,
plus 8 pt. The SVG of an artboard also keeps an object whose effects paint onto the artboard, and an object that holds
an instance when the effects in the symbol's art, or the instance's own, paint onto it.
plus 8 pt. PDF and EPS export and Skip Blank Artboards grow the box of an object with a visible, painted brushed stroke the
same way, by its own widest stroke, and the box of an object that holds one by the largest such room among them (of a
clip group, only its clipping path counts). Print with Ignore Artboards frames the page around the art with the same
room for brushed objects and for instances whose art holds brushed strokes. The SVG of an artboard also keeps an
object whose effects paint onto the artboard, and an object that holds an instance when the effects in the symbol's
art, or the instance's own, paint onto it.
## Expand
@@ -5208,7 +5314,7 @@ The Attributes panel (`window.panel {panel: "attributes"}`, Cmd+F11) reads `attr
objects differ) and `recentUrls`. `attributes.set {overprintFill?, overprintStroke?, showCenter?, imageMap?, url?,
note?, ids?}` sets them in one undo step; `path.setFillRule {rule: "nonZero"|"evenOdd"}` sets the fill rule of the
paths and compound paths, and `path.reverse {reversed?}` makes subpaths run counter-clockwise (true) or clockwise
(false). When every subpath of a path turns alike, its strokes' width profiles (left and right swapped) and pen
(false). When every subpath of a path or compound path turns alike, its strokes' width profiles (left and right swapped) and pen
pressure, tilt, bearing and rotation turn with it, so the strokes draw as before. SVG export wraps an object with a URL in `<a xlink:href>`, and SVG import reads `<a href>` links back.
```json
@@ -5246,8 +5352,19 @@ it. The native format keeps the events and the files.
## SVG filters
Effect › SVG Filters lists the document's SVG filters below Apply SVG Filter… and Import SVG Filter…; choosing one
applies it to the selection. `svgFilter.list {}` returns them as `filters: [{name, xml}]`. Each is one `<filter>`
Effect › SVG Filters lists the default SVG filters and then the document's other filters below Apply SVG Filter… and
Import SVG Filter…; choosing one applies it to the selection
([apply SVG filter effects](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/apply-filter-effects/apply-svg-filter-effects.html)).
The 14 defaults are VectorCraft's own set, written with [SVG 1.1 filter primitives](https://www.w3.org/TR/SVG11/filters.html):
Soft Blur, Heavy Blur, Soft Shadow, Hard Shadow, Glow, Bevel, Emboss, Thicken, Thin, Rough Edges, Grain, Grayscale,
Invert and Hue Shift. Every document lists them, and a document holds none of them until one is applied or edited
there. `svgFilter.apply` with a default's name copies the default into the document in the same undo step (refused
when the document already holds 64 filters), and `svgFilter.define` with `replace` naming a default adds the edited
copy under that name (code equal to the default's adds nothing). The document's filter of a default's name is then
listed in the default's place. `svgFilter.delete` refuses a default the document holds no copy of; deleting the copy
lists the default again, and applying the default again makes the effects that still name it draw with it.
`svgFilter.list {}` returns the document's filters as `filters: [{name, xml}]` and the defaults as
`defaults: [{name, xml, inDocument}]`. Each filter is one `<filter>`
element whose `id` is its name, with 1 to 64 primitives and at most 64 KB of XML (`feTurbulence` octaves up to 10,
`feConvolveMatrix` kernels up to 9 cells a side, `feMorphology` radii and `kernelUnitLength` up to 100 and
`stdDeviation` up to 500 user units, elements nested at most 16 deep). It holds only filter primitives,
@@ -5258,8 +5375,9 @@ a filter after the others, or with `replace` replaces the filter of that name, w
filter has is refused, and the same filter again records no undo step. `svgFilter.delete {name}` removes one, and `svgFilter.import
{path?|svg?}` adds the `<filter>` elements of an SVG file (at most 16 MB, not SVGZ, the first 256 filters) or of SVG text, leaving out the names the
document already has and the filters `svgFilter.define` would refuse (`{added, skipped}`). Each change is one undo
step. `svgFilter.apply {name, ids?|id?, item?}` applies one as the live effect `svg.filter`, as `effect.apply` does,
in one undo step. The effect (`{name, xml, scale, origin}`) names the document's filter: editing the filter changes
step. `svgFilter.apply {name, ids?|id?, item?, index?}` applies one as the live effect `svg.filter`, as `effect.apply` does,
in one undo step; with `index` it puts the filter in the place of the applied `svg.filter` effect at that index of
`item`, as `effect.setParams` does, and an index that is not an applied SVG filter is an error. The effect (`{name, xml, scale, origin}`) names the document's filter: editing the filter changes
what every object that uses it draws, in the edit's one undo step, and an application after an edit takes the edited
filter. Deleting a filter that objects use leaves their effects in place, still named, drawing nothing (their `xml`
empty) until a filter of that name is defined or imported again. A pasted effect that names one of the document's
@@ -5619,6 +5737,25 @@ used, as a transform's does.
{"name":"run_command","arguments":{"command":"object.transformEach","params":{"scaleH":50,"scaleV":50,"strokes":false}}}
```
## Scale and Reflect on a turned bounding box
`object.scale` and `object.reflect` with a named axis act along the selection's bounding box: after a rotation, `sx`
stretches along the box's own width and `sy` along its height, and `"vertical"` and `"horizontal"` are the box's own
axes, until Object › Transform › Reset Bounding Box squares the box to the page. Both take `basis?: "box"|"page"`:
`"box"` is the default, and `"page"` acts along the page's axes. Any other `basis` is an error, and nothing changes.
`origin` is still a page point, and a numeric `axis` is still a line on the page at that angle. Selections whose
objects stand at different angles, and anchors selected with Direct Selection, use the page's axes; with `copy` or
with `objects: false`, the whole objects or their pattern tiles transform, along the box. The journal entry
records the `basis` used, so a recorded action replays in the same frame; a saved step without `basis` (an action or
journal entry recorded before it) plays along the page's axes, as it did when it was recorded. `object.transformAgain` repeats the transform's page matrix, also after Reset Bounding Box. The Scale
and Reflect dialogs, the Transform and Properties panels' flips, the Scale tool and the Reflect tool's default axis
and Shift steps follow the box the same way; a Reflect axis drawn with the tool is the line drawn on the page.
```json
{"name":"run_command","arguments":{"command":"object.scale","params":{"sx":200,"sy":100}}}
{"name":"run_command","arguments":{"command":"object.reflect","params":{"axis":"vertical","basis":"page"}}}
```
## Free Distort on a turned bounding box
`object.distort` maps the corners of `from` (TL, TR, BR, BL) onto `corners`, which are page coordinates. With
@@ -5828,7 +5965,8 @@ commands send `to: "key"` while the selection has a key, else `"artboard"` for o
anchor points, else the Align To choice. The Distribute commands send `to: "key"` while the selection has a key, else
the Align To choice, and while there is a key object or a key guide they send the panel's Distribute Spacing value as
`spacing`. Edit › Keyboard Shortcuts lists them under Align, with no default shortcut, and
`shortcuts.set {id, shortcut}` gives one a shortcut.
`shortcuts.set {id, shortcut}` gives one a shortcut. The Object › Distribute menu lists the eight Distribute commands,
and each item runs its command.
With the Artboard tool chosen, the same buttons and UI commands, and the Control bar's Align and Distribute buttons,
run `artboard.align`, `artboard.distribute` and `artboard.distributeSpacing` on the artboards the tool has selected.
@@ -6199,8 +6337,9 @@ Color on (HDR or Automatic Color Management) and macOS windows are converted by
again. In a Wayland session, and where no profile is set or the profile is not an RGB display profile of at most 1 MiB,
the canvas stays sRGB. The app reads the display again every 10 seconds and when the window moves to another display.
Exports, `ui.render`, the document, panels and thumbnails are not converted; with Proof Colors on, the proof is drawn
first and then converted. The art is converted before it is drawn over the converted paper, so the blend of a
translucent object or an antialiased edge with the paper can differ slightly from a conversion of the blended color.
first and then converted. Translucent art and the antialiased edges of art over an artboard's paper or the
transparency grid are blended with it in sRGB, and the blend is converted; over the pasteboard and in Outline the art
is converted on its own.
`ui.inspect` returns `displayProfile`: `{enabled, display, source, profile, from, applied, reason,
osManaged}`, where `source` is `profile`, `srgbProfile` (a profile that shows sRGB unchanged), `system`, `none`,
`pending` or `unsupported`, `applied` says whether the canvas is converted, and `reason` says why not.
@@ -6329,9 +6468,11 @@ snap the pointer as drawing tools do, so dragging a corner onto another object's
```
Every point a drawing tool places snaps the same way, through one snapper (#506): the Pen's and Curvature tool's
anchors, the Pencil's first and last point, the shape tools' start and dragged corner or radius (`rectangle`,
anchors, the shape tools' start and dragged corner or radius (`rectangle`,
`roundedRectangle`, `ellipse`, `polygon`, `star`, `lineSegment`, `arc`, `spiral`, `rectangularGrid`, `polarGrid`), the
Type tool's click and frame, and a new artboard's corners. The point lands on another object's anchor, centre or
Type tool's click and frame, and a new artboard's corners. The Pencil ignores Smart Guides and Snap to Point and
shows no Smart Guides: its strokes and straight lines follow the hand (Snap to Grid and Snap to Pixel still move
their ends). The point lands on another object's anchor, centre or
path ("anchor", "center", "path"), else lines up with the anchors, edges and centres of the art and the artboards (a
line from the target and "align"); the Pen's lands on the path being drawn too, so a click on its first anchor closes
it exactly. With `mods.shift` the point keeps to its 45° step from the anchor before it (from `constrainAngle`; a box
@@ -6506,7 +6647,8 @@ report warn about it).
Commands take and return lengths in points (the distances of `object.path.offsetPath`, `object.path.simplify`,
`object.path.splitIntoGrid` and `text.setStyle` `size`/`leading` also take a string with a unit, `"5 mm"`). A length
string takes the same arithmetic as the desktop app's fields, where `%` is a share of the value before it:
`"200mm*50%"` is 100 mm and `"50pt+25%"` is 62.5 pt. The units
`"200mm*50%"` is 100 mm and `"50pt+25%"` is 62.5 pt. A length string with more than 128 `+ - * /` signs is not read
as a length. The units
only change what the UI shows and how it reads typed numbers. Preferences ▸ Units has
four: General (rulers, positions and sizes, the Info panel, dialog distances, canvas measurement labels), Stroke
(weights, dashes), Type (font size, leading, baseline shift) and East Asian Type (no field uses it yet). General is the active document's units
@@ -7227,6 +7369,14 @@ is mixed with middle gray by its alpha.
{"name":"run_command","arguments":{"command":"effect.apply","params":{"effect":"distort.glass","params":{"distortion":10,"textureImage":{"path":"/tmp/texture.png"}}}}}
```
Texturizer (`texture.texturizer`) and Rough Pastels (`artistic.roughPastels`) take `params.textureImage` the same
way, as the surface that Scaling, Relief, Light Direction and Invert act on. With `texture: "loaded"` and no stored
texture they draw Canvas.
```json
{"name":"run_command","arguments":{"command":"effect.apply","params":{"effect":"artistic.roughPastels","params":{"relief":12,"textureImage":{"path":"/tmp/texture.png"}}}}}
```
## Linked images
A placed image linked to its file (`file.place {path}`, Link on) records the file's absolute path, its size,
@@ -7512,8 +7662,10 @@ listed in `warnings`. ASCII and binary DXF files are read; DWG files answer an e
## Links panel
`links.list {show?: all|missing|modified|embedded, sort?: name|kind|status}` lists every image in the layers, top
first, as the Links panel does, then every image and placed document in opacity-mask art and in the art of symbols,
`links.list {show?: all|missing|modified|embedded, sort?: name|kind|status|size}` lists every image in the layers, top
first, as the Links panel does (`sort: "size"`, the panel menu's Sort by Size, puts the smallest file first by the
size Link Info shows; rows without one, embedded images and missing files whose size is not kept, come last, and equal
sizes go by name), then every image and placed document in opacity-mask art and in the art of symbols,
graph designs, patterns and stored brushes, one row per stored object (never one per symbol instance, pattern tile or
brushed stroke): `{id, name, linked, status: ok|modified|missing|embedded, format, pixelWidth, pixelHeight, path?,
found?, page?, preview?, note?, owner?}` (`owner`, for an object outside the layer tree:
@@ -7547,6 +7699,12 @@ bounds; `clip` puts it in a clip group of the old bounds when it is larger). The
except for a placed document, which opens in a tab. With the preference `editOriginalSystemDefaults` (Preferences ›
File Handling › Use System Defaults for Edit Original, on by default) off, Edit Original opens a raster image in the app
that made it, PhotoCraft, where it is installed, and anything else in the system's default app (#5236).
The UI command `links.copyInfo {ids?}` (the panel menu's Copy Info) copies the file names of images and placed
documents `ids` to the clipboard, one per line in the order given, and returns `{text, names}`. Without `ids` (or with
`ids: null`, as for the other `ids` commands) it copies the selected ones, and the rows outside the layer tree picked in
the panel, in the order `links.list` gives them; the panel menu passes the selected rows in the order the panel shows
them. An embedded image gives the name its row shows. An id that is not an image or a placed document, a
malformed `ids`, or nothing to copy is an error, and the clipboard is left as it was.
```json
{"name":"run_command","arguments":{"command":"links.list","params":{"show":"missing"}}}
@@ -7902,10 +8060,11 @@ and is missing from the picture" once for all such images.
`document.open` and `file.place` read `.emf` and `.wmf` (placeable or not) into one artboard, the picture's frame, and
one layer: paths with their fills and strokes (pens become strokes with caps, joins and dashes), clipping groups,
images and point type in the font the file names. Records VectorCraft doesn't read (EMF+ drawing among them) are
skipped with one warning. `clipboard.importEmf {dataBase64, center?}` loads an EMF or WMF picture into the clipboard
for `edit.paste`; the app pastes `image/emf` from the system clipboard ahead of text and bitmaps when the platform's
clipboard service offers it (the Windows desktop service doesn't read metafiles yet).
images and point type in the font the file names. An EMF that draws with EMF+ records is read from them. Hatched and
pattern brushes fill with their average color. Records VectorCraft doesn't read are skipped with one warning.
`clipboard.importEmf {dataBase64, center?}` loads an EMF or WMF picture into the clipboard for `edit.paste`; the app
pastes `image/emf` from the system clipboard ahead of text and bitmaps when the platform's clipboard service offers it
(the Windows desktop service doesn't read metafiles yet).
```json
{"name":"export","arguments":{"path":"/tmp/logo.emf"}}
@@ -8094,7 +8253,12 @@ moving the art moves the crop, and deleting it removes it from its asset (an ass
format row of the document's export settings, the ones Export for Screens remembers (`document.exportSettings`); any
`formats`, `preset`, `settings`, `prefix` or `subfolders` given win for that export. `assets.settings.set` changes
those shared settings (not an undo step); its `subfolders: true` sets Export for Screens' Folders to a folder per row,
and `subfolders: false` sets a folder per row back to All in One Folder. `document.exportForScreens {assets: [id…]}` does the same with its own params
and `subfolders: false` sets a folder per row (without `exportPreset`, also a folder per format or item) back to All
in One Folder. Its `openLocation: true|false`
is Open Location after Export, kept in `document.exportSettings` (not in presets). The panel menu's Create Sub-folders and
Open Location after Export run these two (sub-folders sort the pictures; `assets.export3D` writes mesh files in the
folder itself), and its Format Settings… opens Export for Screens on the first row's format
settings. `document.exportForScreens {assets: [id…]}` does the same with its own params
(the dialog's Assets tab). An asset's crop, and the crop of `export` with `selection: true`, is the bounds the
renderer measures for what the art draws, its effects included.
@@ -8183,6 +8347,16 @@ flattened picture every PSD file carries: Bitmap, Grayscale, Duotone, Indexed, R
A document with transparency keeps it. `file.place {path}` links the file like any image (`links.update` reads it
again when it changes); Multichannel files are refused.
A PSD or PSB file that names a clipping path (the saved path its clipping path resource names, in the
[published PSD file format specification](https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/)) places, and
opens as its merged image (`layers: false`, a file with no layers, or a file whose layers can't be read), inside a clip group whose clipping
path is that path, under the even-odd rule, so the image shows only inside it. The clipping path is an ordinary path
that can be edited or released. `file.place` answers `image`, the id of the image in the clip group. The
[import options help page](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-from-other-apps/photoshop-import-options.html)
says that a file imported as one flattened image keeps the document's clipping path when it has one. `links.update`
and `links.relink` read the file's pixels again and leave the clip group and its clipping path as they are. Files that name no clipping path, or name a path
they don't have, come in as a plain image.
`document.open` converts a PSD or PSB file's layers to objects. In
[PSD import options](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-from-other-apps/photoshop-import-options.html),
the option that converts layers to objects keeps as much of the layer structure as it can, merges the layers of a
@@ -8694,6 +8868,34 @@ on again.
{"name":"run_command","arguments":{"command":"type.convertToAreaType","params":{"ids":[42]}}}
```
## Smart Punctuation
`type.smartPunctuation` replaces keyboard characters in the selected type, or in every text object with `scope:
"document"` or nothing selected, in one undo step. Each option is a switch:
- `fiLigatures` (default `false`): ff, fi and ffi become the ligature characters U+FB00, U+FB01 and U+FB03.
- `flLigatures` (default `false`): ff, fl and ffl become U+FB00, U+FB02 and U+FB04. The longest match is taken first.
- `quotes` (default `true`): straight quotes become the Document Setup quotes.
- `spaces` (default `false`): two or more spaces after a period become one.
- `dashes` (default `true`): `--` becomes an en dash and `---` an em dash.
- `ellipsis` (default `true`): `...` becomes an ellipsis.
- `fractions` (default `false`): 1/2, 1/4, 3/4 and the other fractions Unicode encodes as one character (thirds,
fifths, sixths, sevenths, eighths, ninths and 1/10) become that character. A fraction with a digit of any script, a
slash or a decimal point next to it, as in 1/25, 1/2/26 or 1/2.5, is left as it is, also when a change of style falls
inside it. An inline graphic separates the text before it from the text after it, for this option and for `spaces`.
A ligature or a fraction is written only where the run's font has a glyph for it; elsewhere the letters stay. Ruby
stays on the characters it was set on, and no replacement joins characters on both sides of a ruby span's end. A value
other than true, false or null is refused. The reply gives the number of text objects changed and how many of each
replacement were made; with nothing to replace, no undo step is recorded. Type › Smart Punctuation… opens a dialog with
these options, Replace In (Selected Text Only or Entire Document) and Report Results, which lists the count of each
chosen replacement after OK. The dialog opens on the options confirmed last.
```json
{"name":"run_command","arguments":{"command":"type.smartPunctuation","params":{"fiLigatures":true,"spaces":true,"fractions":true}}}
→ {"changed": 1, "replaced": {"fiLigatures": 2, "flLigatures": 0, "quotes": 1, "spaces": 1, "dashes": 0, "ellipses": 0, "fractions": 2}}
```
## Hyphenation options
`text.setFormat` sets the Paragraph panel menu's Hyphenation… options of the selected type (or `ids`, or the
@@ -8972,7 +9174,8 @@ Options). Warichu characters are set at `warichuScale` percent of their size (10
`warichuLines` lines (a whole number from 1 to 6, default 2) inside their line: one above the other in horizontal type
and side by side across the column in vertical type, the first line above or to the right. `warichuGap` is the space
between the lines in points (0 to 100, default 0, document points as `baselineShift`). The stack is centered across the
line on the center of the line's ideographic em box, and the line keeps the height of the characters' own size.
line on the center of the line's ideographic em box, and the line keeps the height of the characters' own size, or
more when the stack needs it: the line's ascent and descent take in the characters of the outer lines.
A stretch of warichu characters is one block that takes the settings of its first character; a character with other
settings, another size or the other kind of leading (Auto or a number) starts a new one, and a tab, a forced line break or a tate-chu-yoko character ends it. In
vertical type a character marked `tateChuYoko` as well is set as tate-chu-yoko at its own size, and two or three digits
@@ -9019,7 +9222,11 @@ menus, and a value typed past 1000 sets 1000; Reset Panel sets both to 0. Charac
in Advanced Character Formats. With Fractional Widths off or System Layout on, each side is set in whole points, and
justification keeps that width. Live SVG text places each character the space moves, and type on a path with
Insert Aki is written as outlines. An SVG saved without Preserve Editing (`preserveEditing`) reopens without the
Insert Aki values; save it with Preserve Editing to keep them editable.
Insert Aki values; save it with Preserve Editing to keep them editable. An `.ai` file's text document keeps both
values, and opening the file reads them back. The stored values are read in thousandths of an em and a negative
value opens as 0; these are VectorCraft's own rules. When a legacy `.ai` save writes type as text, it writes type with
Insert Aki without the values and warns that they are left out. With `outlineText: true` it writes the type as outlines,
which keep the space Insert Aki adds, and does not warn that the values are left out.
```json
{"name":"run_command","arguments":{"command":"text.setRangeStyle","params":{"id":12,"start":0,"end":3,"akiRight":500}}}
@@ -9047,6 +9254,27 @@ as outlines.
{"name":"run_command","arguments":{"command":"text.setRangeStyle","params":{"id":12,"start":0,"end":3,"tsume":50}}}
```
## East Asian glyph forms
`text.setFormat` and `text.setRangeStyle {glyphForm}` set the East Asian glyph form the characters are drawn in
([the help page](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html)): `"traditional"`,
`"expert"`, `"jis04"`, `"jis90"`, `"jis78"`, `"jis83"`, `"fullWidth"`, `"halfWidth"`, `"thirdWidth"` or
`"quarterWidth"`, which draw them with the font's OpenType feature `trad`, `expt`, `jp04`, `jp90`, `jp78`, `jp83`,
`fwid`, `hwid`, `twid` or `qwid` ([feature list](https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist)),
or `"default"`, the font's default glyphs. A character holds one form at a time, so setting one replaces another. The
text keeps its characters, and a character the font has no glyph of that form for keeps its own. Runs that differ only
in their form are shaped as one piece of text. The form is saved only when it is not `"default"`, character styles
carry it, PDF draws the glyphs the canvas draws, and live SVG text writes it in `font-feature-settings`. Another value
is refused with `` `glyphForm` must be one of "traditional", … ``. One undo step, and a `text.setFormat` call that
gives the form the characters already have records none. With Show East Asian Options on, the Glyphs panel menu lists
the ten forms and Revert To Default Forms: a form is checked when every selected character has it, choosing it again
does nothing, and it is disabled when none of the faces that draw the selected text, fallback faces included, has a
feature for it.
```json
{"name":"run_command","arguments":{"command":"text.setRangeStyle","params":{"id":12,"start":0,"end":3,"glyphForm":"jis78"}}}
```
## Composite Fonts
A composite font sets each class of character in a font of its own
@@ -9276,9 +9504,8 @@ the type it changed, how many paragraphs were `converted`, and the characters `i
Type tool edits, `text.convertListToText` ends its typing first and moves its anchor and caret past the characters
inserted at or before them, whether the call comes from the menu, MCP or the control channel.
After Select All in a frame of threaded text, a call without `ids`, `id`, `start` or `end` converts every paragraph of
the story's frames that are neither locked nor hidden. While the story is selected, its locked and hidden frames are
not converted, even when `ids` names them, and neither is a paragraph whose text starts in one. When the call converts
all of its other frames, the whole story stays selected.
every frame of the story, locked and hidden frames included. `ids` naming a locked or hidden frame converts its
paragraphs too. When the call converts every frame of the story, the whole story stays selected.
In threaded type, when the caret's character moved to another frame, the Type tool edits that frame with the caret
next to the same character, and that frame becomes the selection when it was not selected.
@@ -9393,6 +9620,24 @@ not kerned). Off by default and saved only when on. One undo step.
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"proportionalMetrics":true}}}
```
## H or V Style and Roman Italics
With Show East Asian Options on, the OpenType panel menu has H or V Style and Roman Italics
([the help page](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html)). They are the character
feature tags `hkna` and `ital` of `text.setStyle` and `text.setRangeStyle {features, featureChanges}`
([feature list](https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist)). `hkna` draws kana with the
forms the font makes for horizontal type, and in vertical type the shaper applies `vkna`, the forms for vertical type,
in its place. `ital` draws half-width alphanumerics with the font's italic forms. A font without the feature draws the
characters unchanged. Both are on or off only: `hkna=2` and `ital=0` are refused as unknown features, and `vkna` is
not a tag of its own. A menu item is checked when every character the panel acts on has it; choosing it turns the
feature on for all of them, or off when it is checked, each character keeping its other features, in one undo step.
Reset OpenType Features turns both off. Character styles carry them, and Character Style Options › OpenType Features
has an on, off or unset switch for each. Live SVG text writes them in `font-feature-settings`.
```json
{"name":"run_command","arguments":{"command":"text.setRangeStyle","params":{"id":12,"start":0,"end":6,"featureChanges":["hkna","ital"]}}}
```
## Variable fonts
`text.setFormat {fontAxes: {"wght": 650}}` (the selected type, or `ids`) and `text.setRangeStyle {id, start, end,
@@ -10223,7 +10468,8 @@ A canvas-click dialog also has `preview` (initially on): tentative settings draw
```
The Shape Builder tool's options are its Shape Builder Tool Options (dialog `shapeBuilderOptions`):
`openFilledAsClosed` (on: a filled open path bounds a region as if an invisible edge closed it; off: it cuts regions
`openFilledAsClosed` (on: a filled open path bounds a region as if an invisible edge closed it; a path counts as
filled when any of its fill rows is shown, painted and above 0% opacity, whichever row is on top; off: it cuts regions
as an unfilled path does), `pickColorFrom` (`artwork`, the default: a merged shape takes the appearance, opacity and
blending mode of the front-most selected object under the drag's first point, else of the one under its last point,
else of the front-most selected object; `swatches`: the current fill, and the Left and Right arrow keys without
@@ -10331,9 +10577,30 @@ or of a plane widget, cancels it and puts the grid back. The Plane Switching Wid
of the document window (headless: the first artboard's top-left corner); a press on it picks the plane with any tool
while the grid shows (choosing a perspective tool shows it, and `perspective.grid.show` hides it again with the tool
still chosen), and the keys 1–4 (`key` with `"1"`…`"4"`) pick the left, horizontal, right and no plane.
`perspective.widget.options {show?, position?}` (Perspective Grid Options, double-click the tool) hides it or moves
it to `topLeft`, `topRight`, `bottomLeft` or `bottomRight`, kept with the preferences (`prefs.get`/`prefs.set` key
`perspectiveWidget`).
`perspective.widget.options {show?, position?, anchorPoint?, gridlineIntersection?}` (Perspective Grid Options,
double-click the tool) hides it or moves it to `topLeft`, `topRight`, `bottomLeft` or `bottomRight`, and turns the
two targets of Automatic Plane Positioning on or off (`anchorPoint`: Anchor Point of Perspective Artwork;
`gridlineIntersection`: Intersection of Gridlines; both on by default). The options are kept with the preferences
(`prefs.get`/`prefs.set` key `perspectiveWidget`), and a value that is not `true` or `false` is an error.
Automatic Plane Positioning: `perspective.plane.infer {at?: [x, y], tolerance?, clear?}` moves the active plane
through the anchor point of shown art in perspective, or the gridline intersection of a plane that shows, within
`tolerance` points of `at` (6 by default), for now, and hides the other planes. An anchor point within reach wins
over an intersection. With the Perspective Selection tool, `pointer` `move` with Shift over one does the same,
within 6 screen pixels; with a button down, Shift constrains the move as before. `perspective.plane.set` then brings
the plane it makes active through the same point. The plane goes back to its place, and the other planes show
again, when an object is drawn on it or attached to it (`perspective.draw`, `perspective.attach`, or an object
brought into perspective by `perspective.move` or `perspective.transform`; a drag keeps the plane held until it is
committed), when `perspective.plane.matchObject` moves a plane, when the grid hides, when no plane is active, when
the file is opened again, and with `clear` (Escape with the Perspective Selection tool). `perspective.plane.move`
moves the held plane from where it is held and leaves it at its new place, and the other planes show again.
The held point is view state, not an undo step: `perspective.grid.get` reports it as `grid.inferred` (x, y and z
in the grid's frame). `perspective.grid.set`, `perspective.grid.define` and `perspective.grid.preset` keep it
unless the grid they make hides, has no active plane, or would put the plane through the point behind the viewer.
Without params the command reads the state:
`{active, plane, offset, point?, source?: anchor|gridline}`. With `at`, `source` is absent when nothing was within
reach, and the grid stays as it was. It is an error when the grid is hidden, when no plane is active, when both
targets are off, and when `at`, `tolerance` or `clear` is not what the params take.
## Envelopes
@@ -10619,9 +10886,9 @@ How blends interpolate: closed shapes start where they twist least unless `start
interpolate weight, dashes (a solid stroke counts as the dashed one with its gaps closed), miter limit, arrowhead scale,
width profiles and the pen pressure, tilt, bearing and rotation recorded with the Paintbrush (a stroke without a
recording counts as half pressure, half tilt and 0°, as brushes read it; angles turn the shorter way round; a key's
profiles, recordings and arrowheads turn with its path when the blend starts it at another anchor), while
caps, joins, alignment, arrowheads and brushes switch halfway; gradients of one kind with
different stop counts resample to a common set of stops. Groups pair their members in stacking order (members only
profiles, recordings, arrowheads, dashes and gradient along or across the stroke turn with its path when the blend
starts it at another anchor), while caps, joins, alignment, arrowheads and brushes switch halfway; gradients of one
kind with different stop counts resample to a common set of stops, keeping each step (two stops at one offset). Groups pair their members in stacking order (members only
one group has grow out of the other's centre), compound paths stay compound paths, and type, symbol instances and
images interpolate their transforms (type also its colours, stroke weight and size run by run; the words and the
symbol switch halfway). New blends are knockout groups (`object.setProps {knockout}` changes it), drawn on the
@@ -11000,7 +11267,8 @@ lands where it was drawn.
place and paint. Returns `{id, joined, lines}`.
- `addon.cleanup.removeBreaks {kind, ids?}` removes the forced line breaks (`kind: "line"`) or paragraph returns
(`"paragraph"`) in the characters selected with a Type tool, else in all the text of the selected type. After
Select All in a frame of threaded text, it removes them from the text of every frame of the story, as one undo step.
Select All in a frame of threaded text, it removes them from the text of every frame of the story, locked and hidden
frames included, as one undo step.
A break between two words becomes a space; merged paragraphs take the first one's paragraph style. The frames of a
threaded story, selected all together or through Select All, are read as one text, so a break at the end of one
frame before a word in the next one also becomes a space.
@@ -11373,8 +11641,8 @@ A `configure` call that changes no shape records no undo step and returns `{upda
## Cutting Tools add-on
Cutting Tools is a built-in add-on that prepares art for cutting. Its commands are `addon.cutting.bridges`,
`addon.cutting.weed` and `addon.cutting.banner`; in the desktop app they are Object › Plug-ins › Cutting Tools › Stencil
Bridges…, Weed Box and Lines… and Banner Hems and Grommets….
`addon.cutting.weed`, `addon.cutting.banner` and `addon.cutting.nest`; in the desktop app they are Object › Plug-ins ›
Cutting Tools › Stencil Bridges…, Weed Box and Lines…, Banner Hems and Grommets… and Nest Shapes….
### Stencil Bridges
@@ -11495,6 +11763,39 @@ Banner Hems and Grommets… opens the command dialog with Preview.
{"name":"run_command","arguments":{"command":"addon.cutting.banner","params":{"hem":"1 in","grommetInset":"0.5 in","spacing":"2 ft"}}}
```
### Nest Shapes
`addon.cutting.nest {roll?, width?, height?, margin?, spacing?, rotate?, usePreviewBounds?, artboard?, patterns?}` moves
the selected objects close together onto a sheet or a roll of material
([packing problems](https://en.wikipedia.org/wiki/Packing_problems)). The ranges, defaults and rules below are
VectorCraft's own. The sheet starts at the top-left corner of artboard `artboard` (0-based; by default the artboard
under the center of the selection, else the first). It is `width` × `height` points (0 to 1000000 each; 0, the default,
takes the artboard's width or height). With `roll` true it is a roll `width` wide with no end, and `height` is not used.
`margin` (0 to 100000, default 18) is kept clear inside each edge of the sheet; on a roll, inside the sides and the
start. `spacing` (0 to 100000, default 9) is kept between two objects.
Each selected object is packed by its bounding box: with its strokes and effects, including the reach of shadows, glows
and blurs, when `usePreviewBounds` is true (the default), else by its geometry. A group is one object. The tallest
objects go first, each where its bottom edge is highest and then furthest left. With `rotate` true (false by default),
an object may turn 90° clockwise where that puts its bottom edge higher, or where only the turned object fits; the
longest side then decides the order, and a turned object is measured as it is after the turn. A path of a
pixel-aligned compound path is not turned. Objects are moved, and turned with `rotate`, never resized. An object on the
pixel grid moves by whole points and takes 1 pt more room across and down. Objects that do not fit go in a column to
the right of the sheet, from its top, `spacing` from its edge and from each other, in stacking order (back to front).
Objects without bounds, such as an empty group, are not moved. `patterns` moves pattern fills and strokes with the art
(default: General › Transform Pattern Tiles).
All of it is one undo step, and nothing changes when no object moves. The command fails when nothing selected can be
measured, when the margin leaves no room on the sheet, when no selected object fits, with more than 10000 objects
selected, and with bad params. Result: `{placed, unplaced, rotated, skipped, length}`: the number of objects placed,
the ids of the objects put beside the sheet, the ids of the objects turned, the ids of the objects without bounds, and
the length of material used in points, from the sheet's top edge to the margin below the lowest object placed. Nest
Shapes… opens the command dialog with Preview.
```json
{"name":"run_command","arguments":{"command":"addon.cutting.nest","params":{"roll":true,"width":"24 in","margin":"0.25 in","spacing":"0.125 in","rotate":true}}}
```
## Color Effects add-on
Color Effects is a built-in add-on: its effects are colour adjustments in Effect › Color Effects, its command is
@@ -11965,6 +12266,7 @@ the error text. The interactive fallback applies only to ordinary File > Save, n
| Date | Change |
|---|---|
| 2026-11-13 | The Pencil ignores Smart Guides and Snap to Point and shows no Smart Guides; Snap to Grid and Snap to Pixel still move its ends (#7252). |
| 2026-11-13 | `path.freehand`: `closed`, when given, decides whether a stroke that continues a path (`extend`) closes it; the Pencil and the Paintbrush send it by Close Paths When Ends Are Within. |
| 2026-11-13 | Snap to Point with Smart Guides off draws no Smart Guides labels or lines (#7250). |
| 2026-11-13 | Envelopes: a top object's side whose pieces are all one curve split at anchors follows that curve's parameter (#7097). |
+1 -1
View File
@@ -159,7 +159,7 @@ Gujarati interface follow-up (2026-11-14): the catalog and zero/one plural rule
| M1 | Selection, transform, layers, MCP (common command tools, strict keys, session resources and version-aware result hints) | ✅ mostly done (document-tab artwork drag with lossless resource transfer and one target Undo; malformed MCP app replies discard the connection without replay, #1761; bounded control socket waits and connection-slot recovery, #1726; bounded synthetic control input, #1709; no-op anchor Align keeps Undo, Redo and saved state (#1180 follow-up); transform reference point snaps to anchors/centres; rotated objects keep a rotated bounding box and their angle; Free Transform, Scale and Reflect along a rotated box pending; the Transform fields take one Tab each, the W/H link after them) | 4–6 |
| M2 | Drawing tools + smart guides | ✅ mostly done (#3380 deletion creates independent selected path pieces and keeps bounds-fitted gradients in place; endpoint highlighting remains open; Polygon/Star exact-size count persistence, with repeated-drag, tool-switch and Cancel regression coverage; Curvature point markers without direction handles (#1236, in part); Direct Selection segment click/Delete preserves neighboring edges, #1278; Flare, Reshape, Live Corners widget dragging and per-corner radii and kinds landed, on any path's corners; Shaper rough-shape recognition and orientation steps, the remaining Pen modifier nuances; Shaper Groups with editable sources and separate fill/stroke edits landed; custom ruler origin with drag preview, active-artboard reset, relative X/Y fields and native round-trip (#1212); construction widget/refinements pending); a shape just drawn moves by its centre point and turns from the zone outside a corner with its tool still active (#5477); Tangent and straight-segment perpendicular snapping use saved Smart Guides Geometric Guides preferences and Control bar quick access (#2537), including the existing Snap to Glyph and Alignment Guides toggles (#6164); no View submenu; Copy and Cut with the Pen or Curvature tool take the anchors a Cmd/Ctrl-lent Direct Selection picked (#6123) | 12–18 |
| M3 | Paint & appearance (swatches, color, gradient, stroke, appearance, transparency, styles) | ✅ done (M3.7–M3.98): swatches, groups, libraries, tints, Lab spots and Registration; Eyedropper copies transformed character dimensions (#6404) and styles highlighted text only (atomic transparency rejection); Eyedropper reads visible locked and outside-isolation sources (#1251); Color Picker, Color panel, Color Guide, Color Themes, Edit Colors and Recolor Artwork (screen-bounded Edit chip list, #3416); gradients that follow every transform, the annotator, linked stops, freeform gradients, focal points, gradients on strokes and Expand; visible strokes when paint is added to inherited None/zero-width art (#1253); stroke geometry, arrowheads, dashes fitted to corners, stroke on type, width profiles and the Width tool (Shift-double-click enables adjoining adjustment in Width Point Edit; finite-arithmetic follow-up regressions and full CI passed ([Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area))), Scale Strokes and preview bounds; Appearance targeting, container appearance, target circles, Expand Appearance; knockout, isolation, blend accuracy, CMYK blending, overprint, Attributes, masks and clipping, Flatten Transparency; graphic styles, links and libraries; CSS library export preserves fractional linear and radial gradient stop positions. Left: freeform gradients in SVG/PDF, mixed spot/process gradients in SVG and PDF gradients mixing a spot color with RGB or Lab stops export as stops or process colors; PDF export writes dithered gradient fills with spot stops, and Perceptual spot gradients with RGB or Lab stops other than white or more than 4,096 stops on their curve, as images in process colors; confirm the Scale Strokes & Effects default; Recolor model selection is available through the engine and MCP only (#2520); the dialog retains its existing controls; Library panels' lists (swatch, Color Book, brush, symbol and graphic style libraries) fill a taller panel above its footer (#2992; scores and estimates unchanged); Graphic Styles list fills a taller panel above its footer (#2992; scores and estimates unchanged); Swatches list fills a taller panel above its footer (#1638; `the_list_fills_a_tall_panel_above_its_footer`; scores and estimates unchanged); | 4–8 |
| M4 | Files (native, SVG, PDF, raster, Export for Screens, clipboard interop) | Explicit editing-data import keeps oversized capped-page artboards (#6865; estimates unchanged); Save refusal continuation (#6146): ordinary Save offers Save As; unchanged format keeps options, changed format opens its Save options; cancellation/automation isolation; focused UI regressions passed, native acceptance pending; PDF authored ActualText preserved once in editable import (#6165; first-glyph line/style, bounded input; estimates unchanged); PDF export keeps harakat and zero-advance Noto Arabic dot glyphs in one compatible batch per layout line, with per-glyph vertical offsets (#6309). `crates/pdf/src/tests/realtext.rs` (`mark_text_*`) checks decoded cluster mappings, mixed Arabic/Latin/digits, contour placement against outline export, vertical columns, differing path rotations and PDF/X-1a/3/4. Editable-text import preserves authored ActualText separately (#6165), with cross-line/style layout limits; separately styled or differently rotated clusters and external viewer search/copy remain outside this export-only evidence. `.ai`/EPS type from the text document keeps its paragraphs' tab stops (#6147); `.ai`/EPS type from the text document keeps auto leading per line, its hyphenation options, and may show more of the last lines the file sets in an area frame of its own, when growing the frame by its lines' deepest descent plus 0.05 pt shows them (#6137); ✅ done (M4.14–M4.98 on 2026-10-05): Save As/Save a Copy/Revert/templates, Data Recovery and background save, SVG import and export fidelity (units, text, symbols, filters, SVGZ, Preserve Editing, SVG Options), shared guarded PDF writer and windowed reader ([pdf-backend.md](pdf-backend.md)), PDF import (colours, layers, masks, editable text, security) and export (presets, marks, bleed, ICC and output intent, raster effects, subset fonts, PDF/X-1a, X-3 and X-4, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3), Place and Links, Package, EPS and DXF in and out (EPS legacy mesh corners, vector opacity masks and raster page comparison verified; closed SPLINE flags round-trip and circular paths export as native CIRCLE/ARC, #1299), EMF/WMF, TIFF/BMP/Targa/PSD/GIF/PNG-8 export, Print and print presets, slices, Save for Web, TIFF in Export for Screens and Asset Export, clipboard flavours, File Info; AF.1–AF.4 add Affinity import (native layers, artboards, vectors, text and images from Affinity 1–3 files; the embedded preview only as a warned fallback; AF.4 expands current `.af` fixtures and every-board round-trip validation); EPS Save and Save As keep an opened EPS as EPS with its page frame and complete editable VectorCraft document (2026-10-26; G9 import fidelity unchanged). Left: DWG (use DXF), PSD placement as layers, overprint read back from PDF, Affinity export, polish; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; PDF import: glyphs inside a marked-content span with an empty `ActualText` come in as outlines in their fill and stroke, adding no text (#6971; scores and estimates unchanged); | 3–6 (existing scope; Affinity export unestimated) |
| M4 | Files (native, SVG, PDF, raster, Export for Screens, clipboard interop) | Rename artboards in Export for Screens (#7275); Explicit editing-data import keeps oversized capped-page artboards (#6865; estimates unchanged); Save refusal continuation (#6146): ordinary Save offers Save As; unchanged format keeps options, changed format opens its Save options; cancellation/automation isolation; focused UI regressions passed, native acceptance pending; PDF authored ActualText preserved once in editable import (#6165; first-glyph line/style, bounded input; estimates unchanged); PDF export keeps harakat and zero-advance Noto Arabic dot glyphs in one compatible batch per layout line, with per-glyph vertical offsets (#6309). `crates/pdf/src/tests/realtext.rs` (`mark_text_*`) checks decoded cluster mappings, mixed Arabic/Latin/digits, contour placement against outline export, vertical columns, differing path rotations and PDF/X-1a/3/4. Editable-text import preserves authored ActualText separately (#6165), with cross-line/style layout limits; separately styled or differently rotated clusters and external viewer search/copy remain outside this export-only evidence. `.ai`/EPS type from the text document keeps its paragraphs' tab stops (#6147); `.ai`/EPS type from the text document keeps auto leading per line, its hyphenation options, and may show more of the last lines the file sets in an area frame of its own, when growing the frame by its lines' deepest descent plus 0.05 pt shows them (#6137); ✅ done (M4.14–M4.98 on 2026-10-05): Save As/Save a Copy/Revert/templates, Data Recovery and background save, SVG import and export fidelity (units, text, symbols, filters, SVGZ, Preserve Editing, SVG Options), shared guarded PDF writer and windowed reader ([pdf-backend.md](pdf-backend.md)), PDF import (colours, layers, masks, editable text, security) and export (presets, marks, bleed, ICC and output intent, raster effects, subset fonts, PDF/X-1a, X-3 and X-4, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3), Place and Links, Package, EPS and DXF in and out (EPS legacy mesh corners, vector opacity masks and raster page comparison verified; closed SPLINE flags round-trip and circular paths export as native CIRCLE/ARC, #1299), EMF/WMF, TIFF/BMP/Targa/PSD/GIF/PNG-8 export, Print and print presets, slices, Save for Web, TIFF in Export for Screens and Asset Export, clipboard flavours, File Info; AF.1–AF.4 add Affinity import (native layers, artboards, vectors, text and images from Affinity 1–3 files; the embedded preview only as a warned fallback; AF.4 expands current `.af` fixtures and every-board round-trip validation); EPS Save and Save As keep an opened EPS as EPS with its page frame and complete editable VectorCraft document (2026-10-26; G9 import fidelity unchanged). Left: DWG (use DXF), PSD placement as layers, overprint read back from PDF, Affinity export, polish; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; SVG CSS classes survive export and repeated import in all four styling modes (#2480); current-main integration retains event attributes and scripts; PDF import: glyphs inside a marked-content span with an empty `ActualText` come in as outlines in their fill and stroke, adding no text (#6971; scores and estimates unchanged); | 3–6 (existing scope; Affinity export unestimated) |
| M5 | Performance | 🟡 background render + caches + MT done; `vectorcraft-cli bench` and `vectorcraft-cli perf` (budget suite); opaque solid-fill knockout paths draw once in hard-edge/supersampled raster export, with analytical-AA compositing unchanged; file format v2 opens 3× faster (50k paths: 722 → 244 ms); raster effects (glows, shadows, blur, feather) no longer force the whole frame single-threaded (filtered offscreen per effect, verified equal to the single-threaded reference); Craquelure/Mosaic Tiles/Stained Glass distance searches optimized with pixel-invariance checks and the `texture_drag_perf` probe (#1257); effect-heavy demos need a clean-machine benchmark; dirty-region rendering, GPU backend spike pending | 15–25 |
| M6 | Path operations (Pathfinder, Shape Builder, offset…) | ✅ mostly done (live compound shapes: Make, Alt-click a Shape Mode, member modes, Release, Expand (#4522); single-result Pathfinder names and SVG IDs retained (#1293); Divide open-cutting-path geometry, appearance and history verified (#1307; existing segment cap remains); Simplify preview/counts and rewritten-path component selection verified (#1272; the slider opens at the Auto-Simplify level; a Direct Selection part of a path simplifies on its own); Compound appearance and member fill-rule edits verified (#1197); Boolean precision on almost-horizontal edges fixed and the property tests made deterministic; Shape Builder gap detection, large-offset bug open); Shape Builder Gap Detection shares Live Paint’s spatial gap finder and temporary merge bridges (#2526); Custom is capped at 72 pt in the existing options dialog | 3–6 |
| M7 | Type (point/area/path, editing, styles, OpenType, threading, glyphs) | Spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof and the custom dictionary and Ignore All commands and Auto Spell Check with its right-click suggestions, Ignore All and Add to Dictionary, and Spelling › Auto Spell Check in the context menu of type, and the Edit Custom Dictionary dialog (#4936; the Check Spelling dialog remains); Right-aligned area type sets the text after a line's last tab against the right edge, the text before it at its stops (#6147); A line a soft return ends is justified as a wrapped line is (#6137); Indian-language pattern hyphenation preserves syllables in both composers (#4947); 🟡 Hanging punctuation keeps its published amounts under Glyph Scaling, and Standard burasagari counts a hung line start as room; Fullwidth `!` / `?` keep an upright fallback in vertical type; Hyphenation… and Justification… per paragraph (#2310, #2755), Auto Leading also driving the Character panel's Leading, the Alt+↑/↓ leading nudge and Fill with Placeholder Text; Document-point character controls after object/group scaling (#1034; optional font-size preservation remains separate), Character/Paragraph Styles (explicit Auto-leading resets and local leading overrides), per-paragraph paragraph attributes, mixed paragraph alignment indication (headless regressions, full integration CI and visual review pass), Area Type Options (with Width/Height, Auto Size and Shrink Text to Fit; overflow reported to agents and vertical Align), type areas resized by their handles and reshaped with Direct Selection, threaded text, Fit Headline, Glyphs (feature-valued alternates and unencoded glyphs applied in one Undo step while keeping Arabic joining, #5514), OpenType panel (including font-defined Stylistic Sets), Find Font (Document, Recent and System replacement sources, #5217), installed system fonts in every font menu (fonts registered with Windows outside its font folders, the XDG and Flatpak host font folders on Linux, and fonts installed while the app runs, listed when it comes back to the front; searchable; the Character and Properties menus show each family's sample of the selected text, preview it live under the pointer or ↑/↓, show recently applied families first (#3515), filter by script and kind, list a family's styles, and star favourites; the Glyphs panel and Find Font's Replace With use the same menu without the live preview) and found by name on open/import (full-name aliases and Missing Fonts searches verified, #1258; Windows font-service QA pending), variable fonts' named instances as styles, CJK names in the UI, input methods (marked text in place, candidate window at the caret; Japanese IME), Text Wrap (offset, invert, both sides of an object; follows edits), Type on a Path effects and Options with draggable start/centre/end brackets (slide, flip), tab stops + Tabs panel (Above Text floats it over the selected text, its ruler at the canvas zoom so stops line up with the columns, #729), inline symbols in text (beyond parity), Hebrew/Arabic bidirectional text, Paragraph Direction, Every-line/Single-line Composer per object (ragged text too) done; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas tatweels on the base letter's paint (`tests::harakat`); tab leaders, spell check, vertical type pending | 45–60 |
File diff suppressed because one or more lines are too long
+7 -3
View File
@@ -163,7 +163,8 @@ The rest is our reading:
depths where they overlap, with caps on or off. A fill wall in a gap stays in its own depth order. Inflate's
curved surfaces follow this only as closely as their flat facets do. A solid painted with a gradient that has
transparent stops under a light that is not white is drawn with an opacity mask: its faces keep their places in
this order, in runs that each carry the mask, and so do its graphics. A member of a 3D group with its own opacity
this order, in runs that each carry the mask, and a graphic drawn after one of its faces carries the mask too
([Graphics](#graphics)). A member of a 3D group with its own opacity
mask or blending mode draws its fill's and strokes' faces in this order inside that mask or blending mode when
they are opaque with Normal blending. Otherwise each solid is drawn on its own with its opacity and blending, the
lower item's first. With a light that is not white, a puffed, shaded Inflate surface in a solid color takes its
@@ -389,8 +390,11 @@ These rules are VectorCraft's own:
turned, foreshortened by the perspective and twisted and tapered with the face, and clipped to it. It is shaded
with the face's light: each color is multiplied by the light that falls on the face, except in pattern tiles,
which keep their own colors. It is drawn with the
opacity and blending mode of the fill or stroke the face is painted with. A graphic on a face turned away from
the viewer is not drawn.
opacity and blending mode of the fill or stroke the face is painted with. Solids whose fills and strokes have the
same opacity and blending mode share one graphic on each face, clipped to their faces there and drawn after the
last of them. An object's fill and stroke share it when the fill is drawn with an opacity mask too, and the
graphic takes the mask of the solid it is drawn after; members of a 3D group drawn with a mask keep graphics of
their own. A graphic on a face turned away from the viewer is not drawn.
- On the back cap, X runs to the right as the back is seen from behind, so the graphic reads the right way round
there.
- On Revolve a graphic lies on the swept side, which has one face (Front), unrolled flat. X runs around the axis:
+3 -1
View File
@@ -34,6 +34,7 @@ against Illustrator's public documentation; **estimated** unless noted.
| Preserve font sizes while scaling a group | missing | Separate enhancement; scaling still scales text with the other artwork. |
| OpenType panel, Glyphs panel, alternates | done | Highlight Alternate Glyphs preference (#394, off by default) paints pink behind OpenType substitutions on screen when on (required shaping such as Arabic joining forms and the lam-alef ligature, including a lam-alef older fonts file under `liga`, is not highlighted; an optional feature that changes the ligature is); stylistic sets ss01–ss20, stylistic and titling alternates, proportional and lining figures, superscript and subscript are character-style features (`text.setStyle {features}`, #1607); the OpenType panel's Position menu sets superscript, subscript, numerator or denominator glyphs (`sups`, `subs`, `numr`, `dnom`). The OpenType panel's Stylistic Sets menu lists the selected font's `ss01`–`ss20` features, using a font-supplied name where available and `Set N` otherwise; several sets can be active, and selecting an active one removes it. For a mixed-font selection it uses the first selected character's font, a VectorCraft design choice ([public help](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/add-stylistic-sets-to-selected-text.html), [OpenType feature specification](https://learn.microsoft.com/en-us/typography/opentype/spec/features_pt)). The Glyphs panel highlights the character selected with the Type tool (a range's first character, else the one before the caret) and scrolls it into view as the selection moves (#4908; `panels::glyphs` tests); its Show menu lists Unicode ranges and Alternates for Current Selection, the selected character's alternate glyphs from the font's `GSUB` table. A double-click applies the OpenType feature that draws a variant to that character alone, or uses a glyph choice when no supported feature draws it; see the Glyphs panel row below for Entire Font and the joining limits. The glyph-cell pop-up of alternates is not there yet |
| Glyphs panel: alternates as OpenType feature values, unencoded glyphs | done | #5514, as [Replace characters with alternate glyphs](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/replace-characters-with-alternate-glyphs.html) describes (Show › Alternates For Current Selection; replace the selected character by double-clicking the glyph). A character-style feature takes a value (`"salt=2"`, the font's alternate number; `ss01`–`ss20`, `swsh`, `titl` and `aalt` too) in `features` and `featureChanges`, in SVG `font-feature-settings` (`"salt" 2`) and in native files as the plain string. Show › Entire Font applies an unencoded glyph the same way when it is an alternate of the selected character in its word, applies one no feature reaches (`A.salt` in a font with no `salt`) to the selected character as a glyph choice (it may not join), and dims the others with the reason in the tooltip (e.g. `yehfarsi.fina.alt2 belongs to ی (U+06CC) at the end of a word; your selection is ي (U+064A)`); an Arabic presentation form picked there inserts its letter so the word joins. Runs that differ only in their features shape as one piece of text, each run's features over its own characters, so a variant on one letter of an Arabic word keeps its neighbours' initial, medial and final forms and the letters still touch. The panel lists the alternates of the glyph the shaper makes for the selected character in its word (a medial letter shows medial variants), and a double-click applies the OpenType feature that draws the variant (`salt=N` or `ssNN`) to that character alone, in one undo step (`text.setRangeStyle`); a variant no feature reaches falls back to a replacement glyph (`text.setGlyphAlternate`), which its tooltip says may not join. Show › Entire Font also lists the glyphs no character maps, with their name or `gid N` and the feature that gives them. The text crate reads each face's `GSUB` once (alternate sets, single substitutions, the feature that reaches a glyph). Not done: alternates reached through contextual or ligature lookups, and features of the font that the style has no tag for (`cv01`–`cv99`, `hist`). Evidence: `tests::glyph_alternates` in the text crate (a fixture font with a medial teh at `salt=1` to `3`, the neighbours' forms, no gap; Kufi Murabba PIIIX when it is installed), `features::tests::feature_values_are_parsed_validated_and_shaped`, `tests::textedit::feature_values_are_set_replaced_and_saved`, the `panels::glyphs` tests. |
| OpenType panel menu: H or V Style, Roman Italics | done | With Show East Asian Options, as [Format East Asian characters](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html) describes: H or V Style is the character feature `hkna` (kana in the font's horizontal forms, shaped as `vkna` in vertical type) and Roman Italics is `ital` (the font's italic half-width alphanumerics), set with `text.setStyle` / `text.setRangeStyle {features, featureChanges}`, on or off only, and in Character Style Options › OpenType Features ([OpenType feature list](https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist)). |
| Font menu: samples, live preview, filters, favourites, recent families; font matching by any name; Find Font | done | #3515: recently applied families appear first, once each; missing families are omitted and the existing Number of Recent Fonts setting applies; #339; script filter: Japanese, Latin, Thai; the font scan on Linux also reads the folders fontconfig's configuration lists, such as TeX fonts under `/usr/share/texmf/fonts` (#1548, fixes #1529; not verified on Linux); Select Text by Font takes a scope: the document, chosen artboards or chosen layers; Find Font's Find In offers Document, Artboard (the active one) and Layers (the selected ones, else the current one) (#5529, a VectorCraft addition); Replace With Font lists family and style rows, one click setting both, with a search over them; its list scrolls on its own, apart from the document's font list (#4972); wherever the font menu opens, its filters keep the same four rows, and it opens at least wide enough for them and for a family name up to 220 points wide with its sample (up to 60% of the window), each sample filling its row's height (a VectorCraft layout); a filter chosen in the font menu of the Character panel, the Control bar or the Properties panel is on in the others, and the filters are cleared when the app quits ([Font browser overview](https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/font-options-overview.html)); the font menus of the Glyphs panel and of the dialogs that pick a font share the same filters (a VectorCraft choice). |
| Missing glyphs set in an installed font made for their script (Thai, Lao, Khmer, Myanmar first; then any font that has them) | done | Thai: Leelawadee UI, Tahoma, Thonburi, Sukhumvit Set, Noto Sans Thai, TLWG |
| Full font-name matching (OpenType name ID 4) | done | #1258: loaded/cataloged faces and Missing Fonts searches; localized names and variable default faces; Windows font-service QA pending |
@@ -51,7 +52,7 @@ against Illustrator's public documentation; **estimated** unless noted.
| Tab stops and leaders; Tabs panel | partial | Tabs panel and stops with leaders in the model (`text.tabs.set`); a stop drags from where the press landed (#1458); leaders fill the tab's gap, repeated on a grid so they line up from line to line (#1877); Ctrl-dragging a stop (Command on macOS) moves all the stops together; in right-aligned area type the text before a line's last tab sits at its stops, from the left edge, and the text after it against the right edge, as Illustrator's own layout in a user's `.ai` sets a price list (evidence: `tests::right_aligned_area_type_sets_the_text_after_its_last_tab_against_the_edge`; centred lines are still aligned as a whole); in justified area type the text up to a line's last tab keeps its spacing and its tabs their stops, and the text after the last tab takes the line's room to the far edge, VectorCraft's own rule (evidence: `tests::justify::a_justified_line_with_a_tab_spreads_the_text_after_it_to_the_edge` and the other tab tests in `crates/text/src/tests/justify.rs`) |
| Hidden characters (Show Hidden Characters) | done | `type.hiddenCharacters` |
| Spell check (Check Spelling, Auto Spell Check, custom dictionary) | partial | the engine (#4936): `text.spelling.check` lists the unknown words of the document's type, each run in its Language, at the bytes `text.editRange` changes, and `text.spelling.suggest` gives suggestions, both from craft-proof, each paragraph on its own (English from the first dictionary it finds: a Hunspell dictionary in the user's dictionaries folder, the SCOWL dictionary built in from craft-i18n, a system Hunspell dictionary, and last the public-domain Moby word list built into it; other languages from the dictionaries craft-proof finds for them). The custom dictionary commands are done: `text.spelling.addWord`, `removeWord` and `changeWord` (Add, and Edit Custom Dictionary's Add, Delete and Change), and `text.spelling.dictionary` lists the words, saved to `Custom Dictionary.txt` beside the preferences; Ignore All: `text.spelling.ignoreAll` skips a word in the active document while it is open. Auto Spell Check is done: Edit › Spelling › Auto Spell Check (`edit.autoSpellCheck`, off at first, saved with the view settings) draws a red wavy line under the unknown words of the visible type, leaves out type with effects, and follows edits, the custom dictionary and Ignore All. A right-click on an underlined word lists the words its dictionary suggests (each replaces it in one undo step, `text.editRange`), or No Suggestions, then Ignore All and Add to Dictionary, and a right-click on type has Spelling › Auto Spell Check, checked while it is on. Edit › Edit Custom Dictionary… lists the custom dictionary's words, with an Entry field and Add, Delete and Change, applied on Done (Cancel leaves the dictionary as it was). The Check Spelling dialog remains. Evidence: `crates/engine/src/tests/spelling.rs`, `crates/ui-egui/src/canvas/tests.rs` (`auto_spell_check_*`), `crates/ui-egui/src/tests/contextmenu.rs` (the right-click), `crates/ui-egui/src/dialogs/custom_dictionary.rs` (the dialog). |
| Smart Punctuation, Change Case, Find & Replace | done | |
| Smart Punctuation, Change Case, Find & Replace | done | Type › Smart Punctuation… opens a dialog with the ff, fi, ffi and ff, fl, ffl ligatures, Smart Quotes, Smart Spaces, En, Em Dashes, Ellipses and Expert Fractions, Replace In and Report Results, which lists the count of each replacement; ligatures and fractions are written only where the font has a glyph for them. Evidence: `tests::menucmds::smart_punctuation_ligatures_spaces_and_fractions_are_counted`, `smart_punctuation_skips_ligatures_and_fractions_the_font_lacks`, `smart_spaces_keeps_one_space_after_a_period`, `dialogs::smart_punctuation::tests::menu_item_opens_the_dialog_and_ok_reports_the_counts` |
| Alt + numeric-keypad codes while typing (Windows: `Alt+0176` → °) | partial | #5108: on Windows the character Windows posts when Alt comes up after keypad digits is typed at the caret like any other, in the typing's undo step, and the keypad digits typed with Alt run no shortcut. With NumLock off the keypad keys (Insert, End, arrows, Clear, Home, Page Up/Down without the extended-key flag) count as digits too and don't move the caret (`windows_alt_codes` unit tests; neither NumLock on nor off has been tried on a Windows machine yet). `type.insert {code}` (Windows Alt+0 code, Windows-1252 for 128–159) and `{unicode}` (hex code point) insert a character by its code (`menucmds::insert_a_character_by_its_code`). macOS and Linux have no such system convention and egui reports a keypad digit as the top-row digit, so there it does nothing; a floating panel window doesn't receive the character |
| Caret: the arrows and Delete step over whole clusters (Thai consonant with its marks, combining accents, emoji); Backspace removes one character | done | in text with right-to-left characters the arrows move in visual order and stop on both sides of a character drawn with no width, such as a zero width non-joiner |
| Touch Type tool | done | moves, scales and rotates one character through its kerning, baseline shift, scales and rotation (`text.touchType`); a character with a preceding logical character on the same line can move along the line in either reading direction; the first logical character moves only up and down. Positive `dx` follows reading order. These direction rules are VectorCraft's choices. |
@@ -69,6 +70,7 @@ against Illustrator's public documentation; **estimated** unless noted.
| CJK: East Asian Type units (Q, Ha) | done | Preferences › Units › East Asian Type (`unitsAsianType`, Points by default) offers every unit and Q and Ha, a quarter of a millimetre each ([Scripting Guide](https://ai-scripting.docsforadobe.dev/scripting/measurementUnits/): 0.709 pt per Q; [InDesign's unit notation](https://helpx.adobe.com/in/indesign/desktop/layout-and-grid-tools/rulers-and-measure-tools/change-ruler-measurement-units.html): `q`, `h`). The public pages do not say which fields follow it, so no field uses it yet and that waits for a source. Evidence: `q_and_ha_are_a_quarter_millimetre`, `east_asian_type_unit_*` |
| CJK: manual tate-chu-yoko | done | Character panel menu › Tate-chu-yoko (`text.setFormat` / `text.setRangeStyle {tateChuYoko}`), with Show East Asian Options: marked characters stand side by side across one em of a vertical column, squeezed to the em when wider; two or three half-width digits still do so without the mark; horizontal type keeps the mark and does not move them. Tate-chu-yoko Settings… (`ui.tateChuYokoSettings`, `{tcyUpDown, tcyLeftRight}` in points) moves a marked block up or down and right or left as one piece. Evidence: `tests_vertical::manual_tate_chu_yoko_sets_marked_characters_across_one_em`, `tests_vertical::tate_chu_yoko_settings_move_a_marked_block_up_down_left_and_right`, `typecmd::tate_chu_yoko_is_a_character_attribute_saved_only_when_on`, `typecmd::tate_chu_yoko_settings_are_character_attributes_saved_only_when_set`, `dialogs::tate_chu_yoko::tests` |
| CJK: Standard Vertical Roman Alignment | done | Character panel menu › Standard Vertical Roman Alignment (`text.setFormat` / `text.setRangeStyle {standardVerticalRoman}`), with Show East Asian Options, off by default: in vertical type the marked half-width characters (Latin letters, digits and other characters that would lie on their side) stand upright one below the other, each in its upright cell; spaces keep their width and tate-chu-yoko blocks stay across the column; horizontal type keeps the mark and does not change. Character Style Options has its own checkbox for it. Evidence: `tests_vertical::standard_vertical_roman_alignment_stands_half_width_characters_upright`, `typecmd::standard_vertical_roman_is_a_character_attribute_saved_only_when_on` |
| CJK: East Asian glyph forms | done | Glyphs panel menu › Traditional Forms, Expert Forms, JIS 04, JIS 90, JIS 78 and JIS 83 Forms, Monospaced Full-Width, Half-Width, Third-Width and Quarter-Width Forms, and Revert To Default Forms, with Show East Asian Options ([Format East Asian characters](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html)): the selected characters are drawn with the font's OpenType feature for that form (`trad`, `expt`, `jp04`, `jp90`, `jp78`, `jp83`, `fwid`, `hwid`, `twid`, `qwid`; [feature list](https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist)), one form at a time (`text.setFormat` / `text.setRangeStyle {glyphForm}`); a form none of the faces that draw the selected text (fallback faces included) has a feature for is disabled, and Revert To Default Forms draws the default glyphs. Saved natively only when set, carried by character styles, drawn on the canvas and in PDF, and written to SVG as `font-feature-settings`. Character Style Options has no field for it. Evidence: `tests::typo::glyph_forms_draw_the_fonts_east_asian_forms`, `typecmd::area_tests::glyph_form_is_one_character_attribute_saved_only_when_set`, `panels::glyphs::tests::east_asian_glyph_forms_in_the_panel_menu_set_the_selected_characters_form` |
| CJK: warichu | done | Character panel menu › Warichu and Warichu Settings… (`text.setFormat` / `text.setRangeStyle {warichu, warichuLines, warichuGap, warichuScale, warichuAlign}`, `ui.warichuSettings`), with Show East Asian Options ([Format East Asian characters](https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html)): marked characters set at a percentage of their size and stacked on 1 to 6 lines inside their line, one above the other in horizontal type and side by side in vertical type, with a line gap and eight alignments; a block stays whole on one line. Saved natively, carried by character styles, drawn on the canvas and in every export (SVG, PDF and `.ai` write such type as outlines). Type on a path sets the characters at their own size. Evidence: `tests::warichu`, `dialogs::warichu::tests`, `typecmd::area_tests::warichu_is_a_character_attribute_set_saved_and_exported` |
| CJK: composite fonts | partial | Type › Composite Fonts… (`type.compositeFonts.set`), with Show East Asian Options: a font made of a font per class of character (Kanji, Kana, Punctuation, Symbols, Roman, Numerals), each with its own style, size and baseline in percent of the type's size, Kana, Roman and Numerals with horizontal and vertical scale, and Kana with Scale at Center; New, Delete Font, Import… and Export… (`.vccomposite`, `type.compositeFonts.import` and `.export`) ([Creating composite fonts](https://help.adobe.com/archive/en_US/illustrator/cs3/illustrator_cs3_help.pdf#page=347)). Composite fonts are listed first in the font menus, saved with the document, and their type is laid out per class; SVG and legacy `.ai` write it as outlines. Not yet: custom character sets, the Units menu, Show Sample. Evidence: `text::composite::tests::characters_fall_in_their_classes`, `tests::composite::composite_type_draws_each_class_in_its_font`, `compositefonts::tests`, `dialogs::composite_fonts::tests` |
| CJK: ruby | partial | ruby (ふりがな) for live text (`type.ruby`): a reading over a byte range, above (right of the column in vertical type) or below, start/centre/end alignment, size, overhang or squeeze; saved natively, laid out with its line growing, drawn on the canvas, in SVG and PDF (as outlines where ruby is present), in EPS, EMF/WMF and legacy `.ai` (legacy `.ai` writes type with ruby as outlines), and kept by Create Outlines, Expand Appearance and flattening, each glyph where the layout puts it (`typecmd::ruby_tests::create_outlines_keeps_the_ruby_where_it_is_laid_out`) | #633 |
+2 -1
View File
@@ -207,6 +207,7 @@ tools, never checked side by side) isn't captured row by row.
| Ruler guides align to a key object: a click on an object selected with guides makes it the key (one object is enough); Align moves the selected guides to its edge or centre (vertical guides to left, centre or right, horizontal ones to top, centre or bottom) and the key stays put | done | #2143; aligning to the selection or the artboard leaves guides alone |
| Align panel menu › Align to Glyph Bounds › Point Text and Area Text (checked while on): Align and Distribute measure point type or area type by the ink of its glyphs instead of its em box or frame | done | #4231; `alignGlyphBoundsPoint`, `alignGlyphBoundsArea` ([Align text](https://helpx.adobe.com/illustrator/desktop/design-with-text/edit-format-text/align-text.html)); `tests::glyphbounds`, `panels::align::tests::align_to_glyph_bounds_toggles_point_and_area_text`; the Character and Properties panels' Align to Glyph Bounds controls are not done |
| Align and distribute artboards: with the Artboard tool chosen, the Align panel's Align Objects, Distribute Objects and Distribute Spacing buttons, and the Control bar's, move the selected artboards with the art inside them (Move Artwork with Artboard) | done | `artboard.align`, `artboard.distribute`, `artboard.distributeSpacing` ([Align and distribute artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/align-and-distribute-artboards.html)); VectorCraft's own rules: Align to Artboard keeps the active artboard in place and sends the Distribute Spacing value, Align to Key Object is off, a locked artboard stays where it is; `cmd::artboardalign::tests`, `panels::align::tests::the_align_buttons_align_the_selected_artboards` |
| Object › Distribute, below Object › Align: Horizontal Distribute Left, Center and Right, Vertical Distribute Top, Center and Bottom, Horizontal Distribute Space and Vertical Distribute Space, each running what a click on its Align panel button runs (Align To, the key object, the Distribute Spacing value and, with the Artboard tool chosen, the selected artboards) | done | The `align.distribute*` commands; the public page names Object › Distribute beside Object › Align for the Align and Distribute commands ([Align and distribute objects](https://helpx.adobe.com/illustrator/desktop/manage-objects/arrange-objects/align-and-distribute-objects.html)); the items and their order are VectorCraft's own, taken from the Align panel; `menus::tests::object_distribute_runs_the_distribute_buttons` |
| A marquee selects the objects whose paths it touches (anchors or segments): drawn inside a filled shape, over a background, it leaves that shape (Preview and Outline) | done | #1103 |
| Click a segment independently of its endpoint anchors; only its direction handles show | done | #1278: hollow endpoint squares, selected edge emphasized; headless egui checked |
| Drag a curved segment to bend it, a straight one to move its anchors | done | |
@@ -268,7 +269,7 @@ tools, never checked side by side) isn't captured row by row.
| Bounding-box handles: Shift proportional, Alt from centre, rotate outside the corners | done | |
| Rotated objects keep a rotated bounding box and their angle; Reset Bounding Box | done | |
| Vector Halftone Preview keeps the selection box and hides selection marks on the temporary dots | done | #6369; Preview off and Cancel restore the original marks. |
| Free Transform, Scale and Reflect along a rotated box | missing | |
| Free Transform, Scale and Reflect along a rotated box | done | The Scale and Reflect tools, dialogs and commands and the Transform and Properties panels' flips act along the turned box until Reset Bounding Box; `basis: "page"` keeps the page's axes |
| Free Transform widget: constrain, free distort, perspective distort; the key-held variants | done | |
| Transform tools: click sets the reference point (snaps to anchors/centres), Alt-click opens the dialog, Alt-release copies | done | with anchors selected with Direct Selection, the tools, the dialogs and Move transform those anchors alone, about the centre of their box by default (#1408; the default point and whole-object copies are our choices) |
| Move, Rotate, Scale, Reflect and Shear dialogs: live Preview; Up/Down step angles and percentages by 1, Shift by 10 | done | #1412; Preview starts off (our choice: the public documentation gives no default), Move's as it was last confirmed; OK is one undo step and Transform Again repeats it |