Merge main into feat/rotated-box-scale-reflect

This commit is contained in:
dotCooCoo
2026-11-13 11:43:40 -08:00
335 changed files with 20254 additions and 1983 deletions
+2 -2
View File
@@ -107,7 +107,7 @@ the two can still win that race.
| `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` |
| `ui.resize` / `ui.focus` | | |
| `app.open` / `app.save` / `app.export` / `app.quit` | `{path, importer?, params?}` / `{path?}` / `{path?, format?, artboard?, range?, scale?, …}` | `app.open` reads every format `document.open` reads (see `document.formats`) and returns its result (`{index, title, format, warnings, …}`; `warnings` say what didn't come in as it was, such as an EPS shown as its preview image and why its PostScript couldn't be read), or null when a dialog asks first or a library loads. The status bar (`ui.inspect` → `ui.status`) counts the `warnings` and names the first that says reading the file left something out (the notes that make Save ask before writing over it), else the first; with several, hovering it lists them in that order, as many as fit in the window (at most 30), then how many more. After `app.save`, and `app.export` with a `path`, the status bar counts the reply's `warnings` and names the first, and with several, hovering it lists them in the reply's order (a save's notes start with what the format loses). With `importer` (an importer plug-in's format id, `plugin.<id>`) it runs `file.openWith`: the file opens with that importer, also when a built-in format reads its extension, with the importer's parameters `params` (their defaults when omitted; no dialog asks for them). Without `importer`, `params` go to the importer that opens the file, and a built-in format does not read them. `app.export` encodes through the engine's `document.export` (same options; the document keeps its path) and writes `path` through the host; without `path` it returns `{dataBase64, format, bytes}`, as headless mode does. It refuses a key that no format takes, and an unknown key inside a PDF or EPS option object, before it writes anything (`docs/mcp.md`, Parameter keys). `app.save` refuses a key that no save format takes, beside the path or in `options`, the same way. `app.quit`, `file.close` and `file.closeAll` first open a `saveChanges` dialog for each modified document (they return `{"pending": "saveChanges"}`): `ui.dialog.confirm` saves, `ui.dialog.set {field: "discard", value: true}` then confirm discards, `ui.dialog.cancel` cancels the whole close or quit |
| `app.open` / `app.save` / `app.export` / `app.quit` | `{path, importer?, params?}` / `{path?}` / `{path?, format?, artboard?, range?, scale?, …}` | `app.open` reads every format `document.open` reads (see `document.formats`) and returns its result (`{index, title, format, warnings, …}`; `warnings` say what didn't come in as it was, such as an EPS shown as its preview image and why its PostScript couldn't be read), or null when a dialog asks first or a library loads. The status bar (`ui.inspect` → `ui.status`) counts the `warnings` and names the first that says reading the file left something out (the notes that make Save ask before writing over it), else the first; with several, hovering it lists them in that order, as many as fit in the window (at most 30), then how many more. After `app.save`, and `app.export` with a `path`, the status bar counts the reply's `warnings` and names the first, and with several, hovering it lists them in the reply's order (a save's notes start with what the format loses). With `importer` (an importer plug-in's format id, `plugin.<id>`) it runs `file.openWith`: the file opens with that importer, also when a built-in format reads its extension, with the importer's parameters `params` (their defaults when omitted; no dialog asks for them). Without `importer`, `params` go to the importer that opens the file, and a built-in format does not read them. `app.export` encodes through the engine's `document.export` (same options; the document keeps its path) and writes `path` through the host; without `path` it returns `{dataBase64, format, bytes}`, as headless mode does. It refuses a key that no format takes, and an unknown key inside a PDF or EPS option object, before it writes anything (`docs/mcp.md`, Parameter keys). It accepts a key that only another format takes and does not use it: the reply's `warnings` and the status bar name the key ("`quality` was not used: PDF files don't read it"). `app.save` refuses a key that no save format takes, beside the path or in `options`, the same way. `app.quit`, `file.close` and `file.closeAll` first open a `saveChanges` dialog for each modified document (they return `{"pending": "saveChanges"}`): `ui.dialog.confirm` saves, `ui.dialog.set {field: "discard", value: true}` then confirm discards, `ui.dialog.cancel` cancels the whole close or quit |
| `app.export` with `useArtboards` | `{path, useArtboards: true, range? \| artboards?, …}` | Export As: PNG, JPEG and WebP write one file per chosen artboard (default all; SVG does so for a `range`), `<path stem>-<artboard>.<ext>`, and return `{path, files: [path…]}`; a PDF keeps them as pages of `path`. `useArtboards: false` covers the bounds of the visible art. The menu's Export As… is `file.exportAs`: the `exportAs` dialog (`format`, `useArtboards`, `all`, `range`), then a save dialog and the format's options: `pngOptions` / `jpgOptions` / `webpOptions`, `svgOptions`, or `savePdf` for a PDF |
| `ui.dialog.*` on `gradientStop` | `{field: "color" \| "opacity" \| "location", value}` | double-clicking a stop on the Gradient tool's annotator opens its popover (fields `index`, `x`, `y`, `tab`); set `color` (hex), `opacity` or `location` (percentages) and `ui.dialog.confirm` to apply them to the selected stop. `gradient.selectStop {index}` picks the stop the annotator, the panels and Delete/←/→ (`ui.key`) act on |
| `engine.execute` `ui.savePdfDialog` | `{path?, preset?, range?, …document.exportPdf options}` | opens the Save PDF dialog (`file.export.pdf` with no params opens it too). Its fields are the `document.exportPdf` options: set `compatibility` with `ui.dialog.set {field: "compatibility", value: "1.5"}`, a whole section with `{field: "compression", value: {…}}`, the artboards with `__allArtboards: false` and `range`, the visible section with `__section` (General, Compression, Marks and Bleeds, Output, Advanced, Security, Summary). `useArtboards: false` opens it for one page of the visible art's bounds, as Export As with Use Artboards off does: the dialog has no Artboards section and its `__artBounds` field is true. `ui.dialog.confirm` writes `path` (else asks for one), returns `{path, bytes, warnings}` and opens the file when `viewAfterSaving` is set. With `useArtboards: false` it writes the page as `app.export` does and returns `{path, warnings}`, or `{background: true, status}` while Background Export writes it |
@@ -120,7 +120,7 @@ the two can still win that race.
| `app.export` Text and WebP | `{path?, format: txt, encoding?: utf8 \| utf16, lineEndings?: lf \| crlf, selectionOnly?}` / `{format: webp, lossless?, quality?}` | Text writes the stories in stacking order (back to front; a thread once; hidden objects and template layers left out), each line ended by `lineEndings`; `utf16` is little-endian with a byte order mark. Export As → Text opens `txtOptions` (fields `path`, `encoding`, `lineEndings`, `selectionOnly`; OK writes the file). WebP is always lossless: `lossless: false` adds a warning |
| `engine.execute` `document.exportForOffice` | `{}` / `{path?, ppi?: 72 \| 150 \| 300, transparent?, artboard?}` | File → Save for Office Documents…: without params opens `saveForOffice` (fields `ppi`, `transparent`, `artboard`; OK picks the file); with params writes the artboard as a PNG (`path`, else a picked file) → `{path}`, and the status bar shows the export's `warnings` as Save As shows a save's notes. Headless it returns `{dataBase64, width, height, warnings}` |
| 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), `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 |
| 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` still exports the selection in one go, without the dialog |
+2 -2
View File
@@ -125,8 +125,8 @@ On GNOME/Wayland, enable **Preferences › User Interface › System Title Bar**
The window runs natively on Wayland (eframe's `wayland` feature) and on X11. On machines with two GPUs it renders on the one the desktop runs on unless Preferences say otherwise; see [Desktop graphics processor](#desktop-graphics-processor) (#502). The system clipboard (`apps/vectorcraft/src/clipboard.rs`) is arboard with its `wayland-data-control` feature:
- **Clipboard:** under Wayland arboard talks to the compositor through the data-control protocol (wlroots compositors such as Sway and Hyprland, KDE Plasma), so bitmaps (`image/png`), SVG markup and text copied in any app paste (#398). Where the compositor lacks the protocol (GNOME's Mutter, [arboard#223](https://github.com/1Password/arboard/issues/223)), arboard falls back to the X11 clipboard through XWayland, which only holds what X11 apps copied. Where there is no X11 either (the Flatpak on a Wayland session: `--socket=fallback-x11` gives it none), Copy and Paste use the compositor's core data-device protocol on the window's own connection through smithay-clipboard: text only (a copy's text or its SVG markup), and only while the window has focus. smithay-clipboard waits for the owner's data with no limit, so its reads run on a worker thread and the UI thread waits 2 s at most: an owner that never sends its data costs one read 2 s, and later reads return at once until it answers. When a read gets no answer in its 2 s, such as a paste's check whether the clipboard still holds what VectorCraft copied, the paste waits on its background thread for that read's answer, up to 10 s after the read was sent, and pastes the text unless it is empty or what VectorCraft copied. A clipboard neither backend opens is tried again after 1 s, doubling to a minute (a Copy or Paste of the user's at most once a second), and logged once when it goes and once when it comes back: before, the background Paste check reconnected and logged a warning every 250 ms. egui's own text paste into fields goes through smithay-clipboard and works either way.
- **Clipboard owners that never answer (#510):** reading the clipboard waits on the app that owns it: arboard gives up on an X11 owner after 4 s for each format it tries, and a Wayland read waits for the owner to send its data. So on Linux the check that enables the Paste menu items runs on a background thread with a second clipboard handle (`crates/ui-egui/src/clipboard_probe.rs`, installed as `Services::clipboard_probe`), only while nothing is copied in the app and a document is open, and the window keeps drawing. Every arboard read on Linux and the BSDs, including the UI thread's (a paste, and a Copy or Cut checking what the clipboard held), runs on a worker thread with a handle of its own and waits 2 s at most: an owner that never sends its data costs one read that wait, and later reads fail at once until it answers. A paste's bitmap read waits up to 10 s on the paste's background thread, so the UI thread does not wait for it. When one of a paste's reads gets no answer in its 2 s, or a paste follows a read that has not been answered yet, such as the paste's own check whether the clipboard still holds what VectorCraft copied, the paste waits on that thread for that read's answer, up to 10 s after the read was sent, and pastes what it holds. When the answer is what VectorCraft copied, holds nothing to paste, or does not come, the paste takes the internal clipboard. On X11, quitting within 25 s of a read that got no answer waits for that read to end, so the app's own handle is the last one and hands what it copied to the clipboard manager. Windows and macOS keep the check on the UI thread: Windows only asks which formats the clipboard holds, macOS asks the pasteboard server.
- **Clipboard:** under Wayland arboard talks to the compositor through the data-control protocol (wlroots compositors such as Sway and Hyprland, KDE Plasma), so bitmaps (`image/png`), SVG markup and text copied in any app paste (#398). Where the compositor lacks the protocol (GNOME's Mutter, [arboard#223](https://github.com/1Password/arboard/issues/223)), arboard falls back to the X11 clipboard through XWayland, which only holds what X11 apps copied. Where there is no X11 either (the Flatpak on a Wayland session: `--socket=fallback-x11` gives it none), Copy and Paste use the compositor's core data-device protocol (`wl_data_device`) on the window's own connection, and only while the window has focus. One Copy offers the PNG as `image/png`, the SVG as `image/svg+xml` (with Include SVG Code on) and the text as `text/plain;charset=utf-8`, `UTF8_STRING` and `text/plain`; a format with no data is not offered. Paste reads `image/svg+xml`, then text, then `image/png`, and blank text or text that only names a picture gives way to the PNG; the Paste menu item is enabled by the types offered, without reading any data. Whether the clipboard still holds VectorCraft's Copy is told by its data source, which the compositor cancels when another app copies. A read takes 64 MiB at most and fails beyond that. Reads run on a worker thread: text waits 2 s at most on the UI thread, SVG and PNG 10 s on the paste's background thread. An owner that never sends its data costs one read its wait, and later reads return at once until it answers. When a text read gets no answer in its 2 s, the paste waits on its background thread for that read's answer, up to 10 s after the read was sent, then reads the clipboard again unless it holds VectorCraft's Copy. Another app's paste of a Copy is sent on a thread of its own, 8 at once at most; a paste beyond them gets nothing. A clipboard neither backend opens is tried again after 1 s, doubling to a minute (a Copy or Paste of the user's at most once a second), and logged once when it goes and once when it comes back: before, the background Paste check reconnected and logged a warning every 250 ms. egui's own text paste into fields goes through smithay-clipboard and works either way.
- **Clipboard owners that never answer (#510):** reading the clipboard waits on the app that owns it: arboard gives up on an X11 owner after 4 s for each format it tries, and a Wayland read waits for the owner to send its data. So on Linux the check that enables the Paste menu items runs on a background thread with a second clipboard handle (`crates/ui-egui/src/clipboard_probe.rs`, installed as `Services::clipboard_probe`), only while nothing is copied in the app and a document is open, and the window keeps drawing. Every arboard read on Linux and the BSDs, including the UI thread's (a paste, and a Copy or Cut checking what the clipboard held), runs on a worker thread with a handle of its own and waits 2 s at most: an owner that never sends its data costs one read that wait, and later reads fail at once until it answers. A paste's bitmap read waits up to 10 s on the paste's background thread, so the UI thread does not wait for it. When one of a paste's reads gets no answer in its 2 s, or a paste follows a read that has not been answered yet, such as the paste's own check whether the clipboard still holds what VectorCraft copied, the paste waits on that thread for that read's answer, up to 10 s after the read was sent, and pastes what it holds. When the answer is what VectorCraft copied, holds nothing to paste, or does not come, the paste takes the internal clipboard. On X11, quitting within 25 s of a read that got no answer waits for that read to end, so the app's own handle is the last one and hands what it copied to the clipboard manager when one runs. That includes a read of the Paste menu check's handle: quitting waits up to 27 s for the background thread to drop it before the app drops its own, and quits after that wait even when the handle has not been dropped. The app hides its main window before these waits (`hide_before_teardown` in `apps/vectorcraft/src/app.rs`), so an X11 window, an XWayland one included, closes at once; winit cannot hide a native Wayland window, which stays on screen until the waits end. Windows and macOS keep the check on the UI thread: Windows only asks which formats the clipboard holds, macOS asks the pasteboard server.
- **Copied files:** files copied in a file manager (`text/uri-list`; `CF_HDROP` on Windows, file URLs on macOS) paste as the first one that is art (SVG, PDF, EPS, EMF/WMF or a bitmap; a `.ai` or `.ait` file from the editing data it holds, else as the PDF or PostScript it holds), before the clipboard's other formats (which include the files' paths as text).
- **Dropping files on the window does not work under Wayland:** winit 0.30, which eframe 0.37 runs on, has no Wayland drag and drop ([winit#1881](https://github.com/rust-windowing/winit/issues/1881), added in winit 0.31; [egui#1563](https://github.com/emilk/egui/issues/1563)), so no `dropped_files` arrive. Until eframe moves to winit 0.31: copy the files in the file manager and paste them, use File › Place or Open, or open the window through XWayland, where drops work: set **Preferences › Performance › Linux Display Server** to **X11 (XWayland)** and restart (#1151), or start the app with `WAYLAND_DISPLAY= vectorcraft`. The preference (`linuxDisplayServer`, read at startup in `apps/vectorcraft/src/main.rs`, shown on Linux only) has winit open the window on X11 (`EventLoopBuilderExtX11::with_x11`) when the session is Wayland, `DISPLAY` is set and the X11 libraries winit loads are installed (`apps/vectorcraft/src/xwayland.rs` reads `ldconfig -p` and the usual library directories); otherwise the window opens on Wayland and the log says why. Unlike clearing `WAYLAND_DISPLAY`, it leaves the clipboard on the compositor's own.
- **Pens and pen displays under Wayland (#491, #2028):** winit 0.30 doesn't bind the Wayland tablet protocol (`zwp_tablet_v2`; tablet input arrives in winit 0.31, [winit#4318](https://github.com/rust-windowing/winit/pull/4318), [egui#7731](https://github.com/emilk/egui/pull/7731)). Since Plasma 6.3, KWin no longer turns tablet input into pointer input for such apps ([kwin!6336](https://invent.kde.org/plasma/kwin/-/merge_requests/6336), commit [25c65abd](https://invent.kde.org/plasma/kwin/-/commit/25c65abddd95eb87fb892a8594faccbf509ff62f)), and GNOME's Mutter does not turn it into pointer input either (that commit's message): on KWin the pen moves the compositor's cursor over the window, but the app gets no motion or presses and shows none of its own cursors. Until eframe moves to winit 0.31, the app runs under XWayland, which supports the tablet protocol and hands the pen to X11 apps as a pointer: Preferences › Performance › Linux Display Server's Automatic (the default) and X11 (XWayland) When a Pen Is Attached (`x11WithPen`) open the window through XWayland when `/proc/bus/input/devices` lists a pen as the app starts, a device with the `BTN_TOOL_PEN` key and the `ABS_X` and `ABS_Y` axes, and log `pen found: opening the window through XWayland`, as PhotoCraft does (#4967; `VECTORCRAFT_NATIVE_WAYLAND=1` keeps the window on Wayland). For a pen attached after the app started, either run the app under XWayland (Preferences › Performance › Linux Display Server › X11 (XWayland); `WAYLAND_DISPLAY= vectorcraft`, or `env -u WAYLAND_DISPLAY vectorcraft`; for a desktop launcher, put `env WAYLAND_DISPLAY=` before the command in its `Exec=` line), or turn KWin's deprecated emulation back on for the whole session by setting `KWIN_WAYLAND_EMULATE_TABLET=1` in KWin's environment (for example in a file under `~/.config/environment.d/` or in `/etc/environment`) and logging in again. The pen then works as a mouse. Under XWayland the app also reads the pen's pressure (next item); with KWin's emulation on native Wayland the pen has no pressure. Other compositors that don't emulate a pointer for tablets behave the same way.
+1 -1
View File
@@ -180,7 +180,7 @@ VectorCraft choices; an original approximation, never compared with another appl
|---|---|
| Revolve | initial: live, editable profile, rotation and light gizmos, default-on Cap with continuous curved rims, smooth vector shading and round seam joins, SVG/PDF output, Expand Appearance ([revolve.md](revolve.md), #846) |
| Extrude & Bevel, Inflate, Rotate | initial: live, depth, twist, taper, solid or hollow caps, five original bevel shapes, Inflate volume, both sides, twist, taper and cap, Rotate of every fill and stroke, groups and layers turned about one shared axis (#4506), contours that cross, touch or overlap merged first ([three-d.md](three-d.md)), Position presets, rotation and light gizmos, SVG/PDF output, Expand Appearance ([three-d.md](three-d.md)) |
| Materials panel, lighting, ray-traced render | Window › 3D and Materials panel with the object types (Plane, Extrude, Revolve, Inflate) and the Object, Rotation and Lighting settings of the applied effect, and Quick Actions › Expand as Wireframes ([three-d.md](three-d.md#3d-and-materials-panel)); one to ten directional lights with per-light Color (opaque RGB, white by default), Intensity, Rotation, Height, Softness and neutral Ambient Light, Revolve and colored Inflate use smooth vector shading; Extrude & Bevel and Rotate retain planar shading; original Standard, Diffuse, Top Left and Right lighting rigs; hard-edged shadows behind or below the object from the first light ([three-d.md](three-d.md#shadows)); the Materials tab's Graphics, document symbols mapped onto Extrude & Bevel's caps and onto a plane, moved with an artboard handle ([three-d.md](three-d.md#graphics)); no base materials, graphics on curved surfaces, soft shadows or ray tracing |
| Materials panel, lighting, ray-traced render | Window › 3D and Materials panel with the object types (Plane, Extrude, Revolve, Inflate) and the Object, Rotation and Lighting settings of the applied effect, and Quick Actions › Expand as Wireframes ([three-d.md](three-d.md#3d-and-materials-panel)); one to ten directional lights with per-light Color (opaque RGB, white by default), Intensity, Rotation, Height, Softness and neutral Ambient Light, Revolve and colored Inflate use smooth vector shading; Extrude & Bevel and Rotate retain planar shading; original Standard, Diffuse, Top Left and Right lighting rigs; hard-edged shadows behind or below the object from the first light ([three-d.md](three-d.md#shadows)); the Materials tab's Graphics, document symbols mapped onto Extrude & Bevel's caps and onto a plane, moved with an artboard handle, and wrapped around Revolve's side ([three-d.md](three-d.md#graphics)); no base materials, graphics on Inflate or on Extrude & Bevel's walls and bevels, soft shadows or ray tracing |
| 3D (Classic): Extrude & Bevel, Revolve, Rotate | Extrude & Bevel (Classic) initial: live, Extrude Depth, Cap, five original bevel shapes with Height and Bevel Extent In or Out, Position presets and rotation gizmo, Perspective, Wireframe, No Shading, Diffuse and Plastic surfaces with one to ten lights, Light Intensity, Ambient Light, Highlight Intensity and Size, Blend Steps, Black, Custom or no Shading Color, Draw Hidden Faces ([create 3D objects](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/create-3d-objects.html)); Revolve (Classic) initial: live, Angle, Cap, Offset from the left or right edge, Position presets and rotation gizmo, Perspective, the same four surfaces, lights, highlight, Blend Steps and Shading Color, groups and layers revolved about one shared axis (same page); Rotate (Classic) initial: live, every fill and stroke turned as one plane, Position presets and rotation gizmo, Perspective, No Shading and Diffuse surfaces with lights, Ambient Light, Blend Steps and Shading Color ([rotate objects in 3D](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/rotate-objects-in-3d.html)); Map Art: none |
| Turntable (30.0 beta) | none |
| Export 3D objects (OBJ, USDA, glTF) | initial: Asset Export's Export 3D Object collects an object with Extrude & Bevel, Revolve, Inflate or Rotate as a 3D asset, and Export writes its solids as a USDA (the default), USDZ, glTF or OBJ mesh with a material in each solid's paint color (`assets.add3D`, `assets.set3DFormat`, `assets.export3D`); no textures, map art or shading ([3D export help](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/export-3d-vector-artwork.html)) |
File diff suppressed because one or more lines are too long
+10 -4
View File
File diff suppressed because one or more lines are too long
+69
View File
@@ -156,6 +156,75 @@ under 0.9 px RMS from the gradient's contours, 1.13 px when smoothed as edges, a
four corners); `layers::tests` (a ramp, flat colors in palette entries far apart, close flat colors, a
thin flat stroke, colors off the line between their entries, and Grayscale's grays, each read from both
sides of the crack) and `subpixel::tests` (the rule, and one decision for a whole piece from either side).
## Blurred edges
`settled` moved a blend pixel to a side of its edge only when the picture's own colors on both sides lay
within 2 px of it, so the middle of an edge blurred over more than about 4 px kept the palette's
in-between tones and traced as rings of thin paths around the edge. In Color with a Limited palette of
at most 16 colors and in Grayscale with at most 16 grays, `cut_edge::settled_wide` now does the work, by
VectorCraft's own rule (RGB distances, 0 to 255 a channel; Grayscale reads each pixel as its gray). A
blend pixel with no own pixel of its color within 4 px pairs the 8 own colors within 8 px that are nearest
to its color. It takes the nearer side of the pair it is best a blend of when that pair explains it twice
as well as its own color, or when it lies between the two and off the line between them by less than 0.15
of their distance apart. Of two pairs that fit equally well it takes the closer pair. Other palettes,
High Fidelity Photo, Low Fidelity Photo and Black and White keep `settled`.
Measured with the harness below on `main` at `77970c6924`, before and after the change (256 px variants
unless stated): 16 Colors on blurred flat art 9.0 paths to 3.0, 158 anchors to 34, 0.239 px off to 0.069;
6 Colors 6.0 paths to 3.0, 118 anchors to 33, 0.226 px off to 0.071; scanned, 16 Colors 8.0 paths to 3.0
and 0.259 px off to 0.110, 6 Colors 7.5 paths to 3.0 and 0.235 px off to 0.093. On each preset's own
inputs (section 1), 6 Colors went from 4.1 paths to 3.0 and 0.143 px off to 0.101, and 16 Colors from 4.6
paths to 3.0 and 0.145 px off to 0.100. Auto Color does not change. Worse by more than 5% (25 of 211 rows
that moved, most of them variants of the same inputs): askew shapes, odd px 11 to 22 with 6 Colors (paths
4.0 to 4.3, odd colours 0.3 to 0.7), 23 to 26 with 3 Colors, 17 to 22 with 16 Colors and 27 to 30 with
Shades of Gray; thin lines, missed max 23.5 to 45.3 px with 16 Colors, and with Shades of Gray paths 5.3 to
7.3, anchors 36 to 41 and odd colours 3.0 to 5.0; linear gradients with 3 Colors, paths 3.0 to 3.3, and
with 6 Colors with Gradients, paths 1.3 to 1.5 and anchors 13 to 16; two- and three-stop gradients with 6
Colors with Gradients, paths 1.2 to 1.3; gradient and photo with 16 Colors, overlap px 0.0 to 0.2 and
specks 0.8 to 1.0. The tables below are the previous measurement, not a rerun.
Tests on code-drawn images: `tests::a_blurred_edge_is_no_ring_of_a_tone_between_its_sides` (a disc
blurred into a 10 px ramp, 16 Colors), `tests::grayscale_settles_a_blurred_edge_between_its_own_two_grays`,
`tests::grayscale_settles_a_tinted_blurred_edge_by_its_lightness`,
`tests::the_photo_presets_keep_the_tones_of_a_blurred_edge` and `quantize::cut_edge::tests`.
## Staircases in smooth areas
Where a band border in a gradient or a shaded area runs at a slant, its pixels form a staircase whose
steps are two or more pixels long. One piece of outline can run along a sharp edge in the image and then
along such a border with no junction between them. That piece as a whole is not a contour (see above),
and the outline kept a corner at every step. `subpixel::outline` now reads a run of three or more edges of
1 to 8 px (`STAIR_MAX`), each turning one way where it starts and the other where it ends, with no
junction on it, as a staircase when the run's own cracks pass `subpixel::low_contrast`. A run is cut at
any step whose own cracks show half of the step between the two colors or more. The 8 px limit and the
one-half cut are VectorCraft's own values. The run's edges are then steps, as one-pixel steps already
were: the outline has no corner where two of them meet, and each gives one point, its middle, moved by the
mean of its cracks' shifts. Where a run meets an edge that is not a step, the corner between them stays,
so a straight edge longer than 8 px next to a staircase stays straight. A straight edge of 8 px or less
between two turns of opposite sign is read as one more step of the run. Inside a run, two one-pixel steps
between longer ones are read as part of the slope and do not make a notch. A staircase of longer steps,
and one whose cracks read as an edge, keep their corners, and Black and White traces do not use the rule.
The runs are the same from either side of the border.
Measured with the harness below, before and after the change: rows of the "flat colour art" inputs do not
change. On gradient and photo inputs, anchors change in 106 rows, 101 of them fewer, by −64% to +6.4%: for
example 390 to 142 (Shades of Gray, linear gradients), 1,860 to 1,537 (High Fidelity Photo, linear
gradients), 6,766 to 6,542 (High Fidelity Photo, gradient and photo) and 47 to 50 (High Fidelity Photo with
Gradients, blurred). On gradient and photo, 6 Colors, 6 Colors with Shapes, with Auto Grouping, without
Gradients and enhanced each leave a gap of 0.9 px (was 1.2) and an overlap of 1.0 px (was 0.8). Edge ΔE
of Shades of Gray on linear gradients goes from 8.36 to 8.40 (8.39 enhanced), and that of 6 Colors with
Gradients on a blurred input from 2.24 to 2.22; other ΔE and edge ΔE values change by 0.01 or less.
Lines % changes by −2 to +3, grouped % by −1 to +4, upright % by +1 in 3 rows (Shades of Gray on a blurred
input), and specks do not change. On synthetic photos of 800 and 4,000 px traced outside this harness,
the corners of rectilinear staircases per 100 px of outline fall from 0.57 to 0.46 and from 1.09 to 0.94
with High Fidelity Photo. On the 800 px photo, the 95th percentile of the pixel difference with High
Fidelity Photo rises from 6.33 to 6.67, and 16 Colors leaves 2 staircases of steps of 2 px or more where
it left none. The tables below are the previous measurement, not a rerun.
Tests on code-drawn outlines in `subpixel::tests`:
`a_staircase_of_longer_steps_in_a_smooth_area_becomes_the_slope_through_its_steps` (a piece with a
staircase of 2 × 3 px steps in a smooth area, a straight run and the same staircase on a sharp edge: the
first gives its steps' middles and none of its corners, the straight run keeps the corners at its ends, the
second staircase keeps its corners, both regions get the same points, and 10 px steps keep their corners
in a smooth area), `a_staircase_that_runs_on_from_a_smooth_area_into_an_edge_keeps_the_edges_corners`,
`a_staircase_whose_cracks_do_not_read_as_a_contour_keeps_its_corners` (two flat colors),
`a_staircase_step_moves_by_the_mean_shift_of_its_cracks`,
`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`.
## 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
+224 -50
View File
@@ -230,6 +230,10 @@ the suite) keep the agent to the folders you give it:
value and a linked JavaScript file in `svg_scripts` outside them by its file name alone (a JavaScript file given
as a URL is kept), and leaves out the `folder` of `export_settings` (the settings Export
for Screens last used) when that folder is outside them. The document itself is not changed.
- `svgInteractivity.info`, `svgInteractivity.addFile` and `svgInteractivity.removeFile` name a linked JavaScript
file outside the read roots by its file name alone in `files`, as `document.json` does, and `removeFile` takes that
name for it (see SVG interactivity). In a frame a request drives, the JavaScript Files dialog lists such a file by
its file name, and `ui_inspect` gives the file selected in the dialog the same way.
- `plugin.list`, `plugin.safeMode`, `file.recovery.*` and `ui_inspect`'s `scriptReport` leave out the paths
outside the read roots, also those the person or an earlier request put there. In `plugin.list`, the plug-in
folder last loaded is `null` in `folder.folder`, and a failed file in `folder.failed` and a plug-in's `source`
@@ -267,7 +271,8 @@ the suite) keep the agent to the folders you give it:
Links in an opened document that point outside the read roots show as missing.
- An error names a path the client gave as given. A path the client did not give (the document's own file
in a save without `path` and in `file.revert`, also inside `action.batch`, an export preset's folder, the
Scripts folder, the document's folder in `links.relink {copy}`) is named
Scripts folder, the document's folder in `links.relink {copy}`, a New Document profile's file in
`file.newProfiles.edit` and `file.newProfiles.reset`) is named
whole only inside the read roots; outside them the error names it by its file name, or as
`a file or folder outside the read roots` when it has none, and keeps the roots and the reason, for example
`automation path rejected: outside the write root /work/project/out: poster.vectorcraft`.
@@ -630,7 +635,7 @@ sampler.
| `answer_dialog` | `{set?: [{field, value?}], action?}` | Remote only. Sets fields of the app's open dialog in the order given with one `ui.dialog.set {fields, action?}` (a missing `value` is `null`). The app stores all of them or none, then answers that same dialog with `action` in the same request: `confirm` (OK) or `cancel`. Another connection cannot open a dialog between the two steps. When the app refuses the `ui.dialog.set`, for example because no dialog is open, the call is an error and the dialog is not confirmed or canceled. `set` cannot change a `__` field other than the dialog's controls held under a `__` name (such as Save PDF's `__section` or the Preferences `__search`): the `__command` and `__params` of a `confirm` or `command` dialog, and every other `__` field that holds what OK acts on, are refused with "the `<kind>` dialog cannot set protected field `<field>`", so OK runs what the dialog was opened for. `action` alone is sent as `ui.dialog.confirm` or `ui.dialog.cancel`. Give `set`, `action` or both; `set` holds at most 256 fields. `inspect_ui` shows the open dialog and its fields as `ui.dialog`. Returns `{dialog, result}`: the dialog after the fields were set (or `null`) and the reply to `confirm` or `cancel` (or `null`). After `invoke_menu` `app.quit`, `file.close` or `file.closeAll` replies `{"pending": "saveChanges"}`, `{action: "confirm"}` saves, `{set: [{field: "discard", value: true}], action: "confirm"}` closes without saving and `{action: "cancel"}` cancels the close or quit. |
| `invoke_menu` | `{command, params?}` | Invokes a menu item by command id. UI commands such as `view.*` and `window.*` are in `command_list` with `ui: true`; running them needs the desktop app (`--connect`). |
| `open_panel` | `{panel}` | Remote only. `panel` is a panel id (`layers`, `swatches`, `colorGuide`, …, as `window.panel` takes) or its display label (`"Color Guide"`), in any case. |
| `screenshot` | `{path?, scale?, artboard?, window?, max_side?, traceViews?}` | Returns MCP image content (`image/png`, base64) plus a text block. Renders the artboard with the pixels of a PNG `export` with `background: "white"` at the same scale; `traceViews:true` draws Image Trace objects in the view set with `imageTrace.setView`, as in the app's window; `window:true` captures the app window (remote only), as `ui_screenshot` does, and `max_side` (1–4096, window captures only) scales the returned image down as described for `ui_screenshot`. With `path` the backend (the headless session or the app) writes the PNG there too; a window capture written there keeps its full size with `max_side`. |
| `screenshot` | `{path?, scale?, artboard?, window?, max_side?, traceViews?}` | Returns MCP image content (`image/png`, base64) plus a text block. Renders the artboard with the pixels of a PNG `export` with `background: "white"` at the same scale; `traceViews:true` draws Image Trace objects in the view set with `imageTrace.setView`, as in the app's window; `window:true` captures the app window (remote only), as `ui_screenshot` does, and `max_side` (1–4096, window captures only) scales the returned image down as described for `ui_screenshot`. With `path` the backend (the headless session or the app) writes the PNG there too; a window capture written there keeps its full size with `max_side`. An app that does not send the image back writes a window capture without `path` to a file in a new folder inside the system's temporary folder (on Unix only the user can open it); the server reads the file and then removes the folder, also when the capture or the read fails. |
| `open_file` | `{path, importer?, params?}` | Opens any readable file as a new active document: `.vectorcraft`/`.drawcraft`, `.vctemplate`, `.svg`/`.svgz`, `.pdf`/`.ai`, `.ait`, `.eps`/`.epsf`, `.ps`, `.dxf`, `.emf`, `.wmf`, PICT (`.pct`/`.pic`/`.pict`, and a version 2 picture of any extension), HP-GL (`.plt`/`.hpgl`/`.hgl`/`.hpg`, and a file of an extension no other format reads that starts with plotter instructions), PNG (`.png`/`.pns`), JPEG, GIF, WebP, TIFF, BMP (`.bmp`/`.dib`/`.rle`), Targa (`.tga`/`.vda`/`.icb`/`.vst`), PCX, Pixar (`.pxr`), JPEG 2000 (`.jpf`/`.jpx`/`.jp2`/`.j2k`/`.j2c`/`.jpc`), HEIC/HEIF (`.heic`/`.heif`), JPEG XL (`.jxl`), PSD (`.psd`/`.pdd`)/PSB (an image opens as a document of its pixel size; a PSD or PSB file with its layers as layers, or as its merged image through `run_command document.open` with `layers: false`; a JPEG XL file as its first frame). Templates (`.vctemplate`, `.ait`) open as a new untitled document. PDF, `.ai` and SVG files saved with Preserve Editing reopen as the document they carry. The reply is `document.open`'s: its `warnings` say what didn't come in as it was (an EPS whose PostScript can't be read opens as its preview, and the warning names the PostScript error, the operator and the procedure). A file of an extension only an installed importer plug-in lists opens with that importer ([Plug-ins](#plug-ins)); `importer: "plugin.<id>"` opens the file with the importer it names, which must list the file's extension, also one a built-in format reads (in the app, as File › Open With does), and `params` gives that importer's parameters (their defaults when omitted). `run_command document.formats` lists the formats. |
| `save_file` | `{path?, compress?, version?, preview?, precision?, options?}` | Runs `document.save`: the document's own file in its own format (native `.vectorcraft` unless it was opened from or saved as SVG, PDF or a restorable `.ai`; then `warnings` say what that format loses). A path's extension picks the format (`.vectorcraft`, `.vctemplate`, `.pdf`, `.svg`, `.svgz`, `.ai`: a PDF carrying the native document, which reopens editable; `.eps`: by default one file whose bounding box fits the visible art, the first artboard when no art shows; `options.useArtboards: true` makes the chosen artboard the page and, with two or more chosen, the file at the path is the central file of the visible art and each artboard is also written to `<name>_<artboard>.eps` beside it, listed in the reply's `files`). For a native file, `compress: true` gzips it (default: the `useCompression` preference, Preferences › File Handling › Use Compression), `version` (1 to 4) writes it for an older VectorCraft (1 and 2 are never compressed, and `version: 2` with `compress: true` is an error), `preview: true` embeds a PNG of the first artboard, and `precision` (0 to 9) rounds path anchors and handles to that many decimal places for that save only (colors, transforms and type stay exact). `options` passes the format's save options as `run_command document.formats` lists them. When `compress`, `version`, `preview` or `precision` is given both at the top level and in `options` with different values, the call is an error (`` `compress: true` and `options.compress: false` disagree: give the option once ``) and nothing is written. A key in `options` that no save format takes is an error, and nothing is written (see Parameter keys). The save drops each option the written format doesn't read. The reply's `warnings` then name each such option given (for example `` `compress` was not used: SVG files don't read it `` for an `.svg` path, and `` `options.preview` was not used: … `` for a key in `options`; a `.ai` file reads `compress` but not `version`, `preview` or `precision`). For a `.ai` file, these warnings name the kind of `.ai` written: a legacy .ai (version 7) for `aiVersion: "7"`, a non-PDF-compatible .ai for `pdfCompatible: false`, and a PDF-compatible .ai otherwise, including a save that reuses the options the document was last saved with. `run_command document.save` and the other save commands refuse any option given beside the path and in `options` with different values, and give the same warnings for a top-level `compress`, `version`, `preview` or `precision` and for each key in `options` that the format doesn't read. The headless session and the app receive the same `app.save` params, and the app is also sent `wait: true`: the call returns once the file is written, or with the error (such as a path outside the write root), even with Background Save on. 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 Background Save preference. Location refusals return `READ_ONLY_LOCATION: <reason>` without a fallback panel for automation; see [Save location errors](#save-location-errors). |
| `export` | `{path?, format?, scale?, artboard?, range?, selection?, outlineText?, options?}` | `svg`, `svgz`, `pdf`, `ai` (the PDF-compatible `.ai` that Save writes, which carries the document when every artboard is written in order), `eps`, `dxf`, `emf`, `wmf`, `hpgl` (a `.plt` plot file for plotters and cutters), `png`, `jpg`, `webp`, `gif`, `png8` (an indexed `.png`), `tiff`, `bmp`, `tga`, `psd` (layered), `txt` (the document's text), `vectorcraft` or `template` (a native template). The tool's `format` enum and `document.formats` list them, generated from the engine's format table. When `format` is omitted, it comes from the path's extension. PDF and `.ai` write one page per artboard: all of them, or `artboard` (0-based) / `range` (`"1-3, 5"`, 1-based). SVG and SVGZ write one file per chosen artboard. EPS writes one file of the visible art; with `artboard`, `range`, `options.artboards` or `options: {"useArtboards": true}` it writes one file per chosen artboard, after a central file of the visible art when there are two or more, and `useArtboards: false` keeps the one file. Rasters, DXF, EMF, WMF and HP-GL write one artboard, or one file per chosen artboard with `options: {"useArtboards": true}`. Rasters also take `options: {"rect": [x, y, width, height]}` (page points, y down): that area of the page at the given `ppi`/`scale`, without an artboard and without changing the document; it is refused with `artboard`, `artboards`, `range` or `useArtboards`, with a non-positive or non-finite size, and for non-raster formats. `options` carries more format options (e.g. `{"quality": 80}` for JPEG). `selection: true` exports the selected objects cropped to their bounds (the reply adds their `bounds`, and reports `format` and the encoder's `warnings` as a whole-document export does); `outlineText: true` writes SVG text as paths. Template layers are left out, live effects are kept, and exporting `vectorcraft` never changes the document's path. Without `path` the bytes come back as `dataBase64`. Both backends run the same `document.export` call. |
@@ -758,9 +763,31 @@ designs keep their own paint. A row that had no
paint before the command and has none after it does not change the paint the graph stores. `paint.setFill`,
`paint.setStroke`, `paint.none` and the commands that apply one paint kind to the other store the topmost row of the kind
even when it is hidden, and store a stroke set on a band. `stroke.set`, `paint.editGradient` and the recolor commands do
not store a series' paint. An `appearance.*` command or `graphicStyle.apply` given a series group's id, and
`graphicStyle.redefine` on a series group linked to the style, edit the group's own rows, and the graph does not store
them.
not store a series' paint.
`appearance.addFill`, `appearance.addStroke`, `appearance.setItem`, `appearance.removeItem`,
`appearance.duplicateItem`, `appearance.moveItem`, `appearance.clear`, `appearance.reduceToBasic`,
`appearance.showAllHidden`, `appearance.transfer` (as its `target`), `graphicStyle.apply`, and `transparency.set` with a
fill or stroke `item`, given a series group's id or the selected series group, edit the paths directly in the group as
if each had been given, and the group keeps no rows of its own. A series group with no path directly in it, for example
one drawn with a column design, and a series group that has a fill or stroke row of its own, as a document saved by an
earlier version can hold, are edited themselves by these commands, and `appearance.moveItem` with `from: "contents"`
moves the group's own Contents row. `appearance.clear`, `appearance.reduceToBasic`, `appearance.showAllHidden` and
`graphicStyle.breakLink` edit the group as well as its paths. `graphicStyle.apply` without `add` and
`appearance.transfer` onto the group clear the group's own effects, opacity, blend mode, isolation, knockout and style
link, and `graphicStyle.apply` with `add` unlinks the group. With `appearance.transfer`, a `source` that is one of the
target group's paths keeps its look on a move, and a `source` that is the group's only path is an error.
`graphicStyle.redefine` gives the style to the paths directly in a series group linked to it, whether or not the group
has rows of its own, links them, and clears and unlinks the group; the source and the paths linked to the style whose
look was edited keep their look. Read as a source, by `graphicStyle.new`, `graphicStyle.redefine` or
`appearance.transfer`, a series group that has no fill, stroke, effect, opacity, blend mode, isolation, knockout or
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.
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.
`text.setFormat` without `start` and `end`, on a graph or on its labels, stores the character and paragraph attributes
it sets for each kind of label it reaches (value axis numbers, category labels, legend labels), and every label of that
@@ -1040,7 +1067,8 @@ and `flattener` options over it (as `object.flattenTransparency` takes them); im
transparency and rasterized areas are clipped to their regions. Layers the file leaves out (non-printing ones, unless
`includeNonPrinting` or `createLayers` is set) are left out of the flattening. A file that would still have transparency fails the
export. PDF/X-1a and PDF/X-3 files are PDF 1.3 files too.
A page with a side longer than 14,400 pt is written with a `UserUnit` (PDF 1.6): its longer side divided by 14,400,
In PDF/A-2b files, `.ai` saves and files written with `longPagesInPoints: false`, a page with a side longer than
14,400 pt is written with a `UserUnit` (PDF 1.6): its longer side divided by 14,400,
rounded up to the next thousandth, with the page's sizes and coordinates in those units, so that no side is longer than
14,400 units (ISO 32000-1, Annex C.2). A PDF 1.5 file with such a page is written as PDF 1.6, with a warning, and
keeps PDF 1.5's encryption (128-bit RC4). A PDF/A-2b file at PDF 1.4 with such a page is written as PDF 1.6 too, with a
@@ -1049,9 +1077,10 @@ unless the page is too narrow for that (below)
([veraPDF rule 6.1.13-11](https://github.com/veraPDF/veraPDF-validation-profiles/wiki/PDFA-Parts-2-and-3-rules#rule-6113-11);
PDF/A-2 files may declare PDF 1.0 to 1.7, [rule 6.1.2-1](https://github.com/veraPDF/veraPDF-validation-profiles/wiki/PDFA-Parts-2-and-3-rules#rule-612-1)).
PDF/X files and other PDF 1.3 and 1.4 files write such a page in points, and apps
that keep to the limit cut it to 200 in. `longPagesInPoints: true` (off by default; "Write pages longer than 200 in at
that keep to the limit cut it to 200 in. `longPagesInPoints` (on by default; "Write pages longer than 200 in at
full size in points" in the Save PDF dialog's General section) writes such a page in points at every PDF version, without
a `UserUnit`, and a PDF 1.5 file stays PDF 1.5; the warning then says the page is written in points. The option applies
a `UserUnit`, and a PDF 1.5 file stays PDF 1.5; the warning then says the page is written in points. Readers that
ignore `UserUnit` show such a page at its full size. The option applies
only to files without a standard: PDF/X files write such a page in points anyway, and PDF/A files keep the `UserUnit`, which
keeps their page boundaries within PDF/A-2's limit of 14,400 units (ISO 19005-2, 6.1.13), a PDF/A file at PDF 1.4
included (written as PDF 1.6). The dialog disables the
@@ -1255,7 +1284,7 @@ canvas draws it as pixels, out to the miters, caps and arrowheads of a stroke. P
have no diamond gradient: one of opaque stops is written as four linear gradients, one for each quarter of the
square, clipped to the shape (PDF as axial shadings). In PDF and SVG, one with translucent stops, a dithered fill or
a square with no area is an image, and so is a Perceptual one, except in PDF when its stops are spot colors that the
Method paragraph below describes. SVG writes one on type characters as a radial gradient through the square's
Method paragraph below describes. One mixed around the hue wheel is always an image in PDF and SVG. SVG writes one on type characters as a radial gradient through the square's
corners. PDF import reads a function-based shading (type 1) whose color is the same along each square about the
origin of its space, with corners on its axes, as a diamond gradient. CSS writes a radial gradient through the
square's corners. A swatch library saved as CSS gets four `linear-gradient` layers, one for each quarter of the box,
@@ -1299,7 +1328,7 @@ change its colors least. Each run keeps its ends; sharp steps and swatch-linked
but a run with more of them than its share of the limit loses some. The same applies to a gradient along or across a
stroke, and to CSS's conic gradient, past 16,384 stops, and to Outline Stroke's mesh past 1,024. For CSS the 16,384
counts the gradient's stops before they are written: CSS then adds a stop for each midpoint, and for a Perceptual
gradient stops along each span's curve, so the written gradient can hold more. PDF
gradient or one mixed around the hue wheel stops along each span's curve, so the written gradient can hold more. PDF
import finds a sharp step between two colors in an axial or radial shading by halving the spans between its samples,
so the step reads back where it is in the file (to 1/64 × 2^-14 of the axis) instead of blurred across the samples
around it. A shading takes at most 16,384 samples, so the steps past the point where that budget runs out stay
@@ -1328,10 +1357,26 @@ stops whose other stops are all paper white or written in CMYK or gray, that is
does not have too many stops (a diamond as four such shadings). An export that converts colors to a CMYK destination profile writes RGB and Lab stops in CMYK, and so does a
CMYK document unless the export converts colors to an RGB destination profile. PDF writes tints of one spot ink as a Separation shading in that ink, and spot inks mixed with other spot, CMYK
or gray inks as a DeviceN shading.
`hue-clockwise` and `hue-counterclockwise` (VectorCraft's own methods) mix a linear, radial, conic or diamond
gradient's colors around the hue wheel: hue, saturation and brightness each change evenly from one stop to the next.
With `hue-clockwise` the hue angle falls (red, magenta, blue, cyan, green, yellow, red), which is clockwise on the
app's color wheels, and with `hue-counterclockwise` it rises. The angle keeps one direction between two stops, as the
fixed-direction rules of [CSS Color 4](https://www.w3.org/TR/css-color-4/#hue-interpolation) do, so red to blue passes
through green one way and through magenta the other. Two stops whose hues are less than half a color level apart
count as one hue and keep it, so a color and a tint or shade of it do not turn the wheel; their saturation and
brightness still change evenly, which is not the mix `classic` gives. A stop whose RGB numbers are less than half a
color level apart is gray: it takes the hue of the stop it mixes with, and a black stop takes that stop's saturation
too. SVG and PDF exports write such a gradient as an RGB image at the Document Raster Effects resolution, in a CMYK
document and with CMYK stops too, and with spot color stops (with a warning). EPS writes it as a shading in process
colors, and a legacy `.ai` file and the `.ai` editing data get the colors between its stops as stops of their own, in
process colors (at most 256, with a warning). `reverse` turns a hue method the other way with the stops, so the ramp
keeps its colors, and a Reflect spread mirrors the colors of each run. The names also read in any case, and with a
space or an underscore for the hyphen, or without it.
`paint.setFill`/`paint.setStroke` take it in `gradient` too:
```json
{"name":"run_command","arguments":{"command":"paint.editGradient","params":{"method":"perceptual"}}}
{"name":"run_command","arguments":{"command":"paint.editGradient","params":{"method":"hue-clockwise"}}}
```
Freeform gradients: `paint.editGradient {kind: "freeform"}` places four or more colour points inside each selected
@@ -1441,7 +1486,9 @@ preferences (the folder the desktop app uses, so both see the same ones; `VECTOR
working spaces and the file stay as they are. The profiles in use are those of the document in front, after
`file.new`, `document.open`, `document.activate`, `file.close`, `file.revert`, `edit.undo`, `edit.redo` and a
failed `command.batch` too; with none assigned, or no document open, the working spaces are. The `workingCmyk`
target of `view.proofSetup` simulates the working CMYK space in such a document too.
target of `view.proofSetup` simulates the working CMYK space in such a document too. `color.convert`'s
`outOfGamut` and `deltaE`, `color.gamutCheck`, and the out-of-gamut warning of the Color panel and Color Picker
check against the assigned CMYK profile, else the working CMYK space; the warning updates when either changes.
Not remembered: the document, anything else. Nothing is read or written when
`VECTORCRAFT_NO_PREFS` is set, and a server confined with `--automation-read-root` or
@@ -1580,8 +1627,18 @@ hides the dock, no panel is checked, and the panel shows again with the dock in
Control bar stay as they are.
The export commands limit those keys. `document.export`, `document.exportSelection`, `document.serialize`, `app.export`
and `file.export` refuse a key that no format takes, and ignore a key that only another format takes
(`{"format": "pdf", "quality": 80}` writes the PDF). `document.exportPdf`, `document.pdfSettings` and `pdf.preset.save`
and `file.export` refuse a key that no format takes. They accept a key that only another format takes and do not use
it: `{"format": "pdf", "quality": 80}` writes the PDF, and the reply's `warnings` name the key
("`quality` was not used: PDF files don't read it"), in the wording of a save's note. The warning names the kind of
file written. A legacy `.ai` file (`aiVersion: "7"`) reads `outlineText`, `hiddenObjects`, `transparentImages`, the
artboard choice and `useArtboards`, and beside `aiVersion` no other option
("`compress` was not used: legacy .ai (version 7) files don't read it"). The other `.ai` files don't read those three
options or `preserveEditing`, which is always on for a `.ai` file. An export doesn't read `separateArtboards`, or
`range` for a `vectorcraft` or `template` file: a save reads them to write each artboard to a file of its own. In the
desktop app, `app.export` and `file.exportAs` give these warnings in the reply and the status bar
when automation asked for the export. An `app.export` that returns before the file is written (`background: true`)
gives them in that reply. An export that first asks a question (the one before replacing a file) and that a person
then answers gives none. `document.exportPdf`, `document.pdfSettings` and `pdf.preset.save`
refuse a key that PDF doesn't take, `document.exportEps` one that EPS doesn't take and `document.exportDxf` one that DXF
doesn't take. Each of them refuses an unknown key inside an option object it takes (`marks`, `bleed`, `compression`,
`output`, `advanced`, `security`, `flattener`, `svg`), naming its path:
@@ -2027,7 +2084,7 @@ VectorCraft's own, and `vc.api.has("dollar")` is true where they are available:
| `global` | the global object |
| `hiresTimer` | the microseconds since the run last read it, or since the run started; `0` on the web |
| `level`, `memCache` | `0`; setting `memCache` throws |
| `line`, `stack` | the line of the code that reads it; the running code, outermost first, one a line as `name:line` (`[global]` for the script's own code, `[anonymous]` for a function without a name), at most 64 lines |
| `line`, `stack` | the line of the code that reads it, counted in the file that code is in (the script, a file it included or a file `$.evalFile` ran, in this run or an earlier run of its named engine); the running code, outermost first, one a line as `name@file:line` (`[global]` for the script's own code, `[anonymous]` for a function without a name; the file as `$.fileName` gives it, or the run's name for a script given as `source`), at most 64 lines. Code that `eval` or `Function` compiled keeps the line of the source they compiled, in the file `[eval]`; code whose place a named engine no longer keeps is at line `0` of `[unknown]` |
| `locale` | the interface's language as a locale such as `de` or `pt_BR` (`en` when the interface follows the system); a script can set `ll` or `ll_RR`, and `null` or `""` sets the interface's again |
| `localize` | `false`; while `true`, `String(object)` of a localization object gives its text for `$.locale` |
| `os` | `Windows`, `macOS`, `Linux`, `FreeBSD`, `Unix` or `Web`, without a version |
@@ -2485,8 +2542,8 @@ them again with the key object. When the call changes nothing, it records no und
Other `object.setProps` calls leave the selection unchanged.
`object.unlockAll` and `object.showAll` unlock or show every locked or hidden object in the document and select
them → `{count}`. In isolation mode they take only the objects inside the isolated object. With `scope: "group"` they
take only the objects inside the groups that hold the selected objects:
them → `{count}`. Ruler guides locked or hidden on their own are counted, and are selected as guides. In isolation
mode they take only the objects inside the isolated object. With `scope: "group"` they take only the objects inside the groups that hold the selected objects:
for each selected object, the group nearest above it, or the object itself when it is a group. The objects of a
group inside another such group are counted once. With no selected object in a group, the call returns the error
"nothing selected is in a group" and changes nothing. `scope: "document"` is the default, and any other value is
@@ -2791,12 +2848,22 @@ vertical guide, the y of a horizontal one, in points) → `{index}`. With `artbo
artboard guide: it runs across that artboard only, moves with it (`artboard.move`, a pure move in
`artboard.setProps`, `artboard.rearrange`), scales with it (`artboard.setProps {scaleArt}`), is copied with it (`artboard.duplicate`, `artboard.move {copy}`) and is
deleted with it (`artboard.delete`); without, a canvas guide runs across the whole canvas. `guide.list` →
`[{index, vertical, pos, selected, artboard?}…]` (`artboard` only for artboard guides).
`[{index, id, vertical, pos, selected, artboard?, layer?, hidden, locked, shown, editable}…]` (`artboard` only for
artboard guides; `id` is the guide's node and `layer` the layer it is on; `hidden` and `locked` are its own state,
`shown` and `editable` also take its layer into account). `layer.setProps {ids: [id…], visible?, locked?}` hides or
locks guides on their own, as the eye and lock on a guide's row in the Layers panel do; a hidden guide is not drawn
and does not snap, and a locked one is drawn and snaps but can't be selected, moved or deleted.
`guide.select {indexes: [index…], toggle?}` selects guides on their own (the art is deselected; `toggle` adds or
removes them) → `{selected}`. `guide.move {index, pos}` puts one guide somewhere, `guide.move {dx?, dy?, copy?}`
moves the selected ones (vertical guides by `dx`, horizontal ones by `dy`; `copy` leaves them and selects the
moved copies), and `guide.remove {index?}` deletes one guide or the selected ones → `{count}`; each is one undo
step. With guides selected, `edit.clear` (Delete) deletes them and `object.nudge` (the arrow keys) nudges them.
moved copies), and `guide.remove {index?, indexes?}` deletes one guide, the guides `indexes` (not with `index`; an
index given twice counts once; a hidden or locked guide among them is an error and nothing is deleted) or the
selected ones → `{count}`; each is one undo step. An `indexes` list of `guide.select`, `guide.remove` or
`guide.setLayer` holds at most 10,000 entries; a longer one is an error. With guides selected, `edit.clear` (Delete) deletes them and `object.nudge` (the
arrow keys) nudges them. Object › Lock › Selection (`object.lock`) and Object › Hide › Selection (`object.hide`) lock
or hide the selected guides on their own, with the selected art, in one undo step, except in isolation mode.
`object.unlockAll` and `object.showAll` with the default scope, outside isolation mode, also unlock or show the
guides locked or hidden on their own and select them as guides.
View › Guides › Lock Guides (`view.guides.lock`) deselects them and the paths made into guides, in every open
document, and refuses these commands until unlocked. While it is on, a command that selects art, such as `edit.undo`,
`edit.redo`, `object.exitIsolation`, `object.unlockAll`, `object.showAll` or `edit.paste`, leaves the guides out.
@@ -2986,6 +3053,13 @@ already walks Image Trace. Short agent entry points:
Tilt goes from its first value with the pen lying flat to its second upright, and on Bearing or Rotation from its
first value at −180° to its second at 180°. A stroke without that input draws the brush's own value (a Scatter
brush's value half way between the two).
- Freehand Fidelity: the `fidelity` of `path.freehand` and `brush.freehand` is in points, 1.5 pt when left out. A
value must be from 0.000015625 pt (0.01 / 640) to 100 / 0.0313 pt (about 3194.89 pt), and a value outside that range
is rejected with an error. The Pencil and the Paintbrush give Fidelity in screen pixels, 0.01 to 100 px, and send it
divided by the zoom (0.0313 to 640) and kept within that range, so a stroke is fitted at the same tolerance on screen
at every zoom. A stroke
that continues a path (`extend`) closes it when it ends within four times the Fidelity of the path's other end, at
least 6 pt and at most 400 pt. The Blob Brush's `path.blob` Fidelity stays in points, 0.01 to 100 pt.
- Variables: `variable.list`, `variable.define {name, kind: text|visibility|image|graph}`, `variable.bind {variable, ids?}`,
`variable.makeTextDynamic`, `variable.makeVisibilityDynamic`, `variable.makeImageDynamic`, `variable.makeGraphDynamic`
(`variable.bind` and `variable.makeTextDynamic` refuse type that already has another text variable and name it),
@@ -3126,17 +3200,24 @@ already walks Image Trace. Short agent entry points:
would give the set more than 1,000 actions (`the action set file holds more than 1,000 actions`) or a file larger
than 8 MiB (`the action set would be larger than 8 MiB`), and then no recording starts. The panel's Create
New Action button and menu's New Action… open the `actionNew` dialog, whose Record runs this.
- Actions: `ui.action.record {set?}` starts recording a new action in the chosen set, or the current set when `set`
is left out, and returns `{set, name}`. With no sets, it creates Set 1. The action gets the next available
`Action N` name in that set. The Actions panel's Begin Recording button runs this command. An invalid set or an
existing recording is an error and leaves the sets and recording unchanged, and so is a set where the new action
- Actions: `ui.action.record {set?, action?, after?}` starts recording a new action in the chosen set, or the current
set when `set` is left out, and returns `{set, name}`. With no sets, it creates Set 1. The action gets the next
available `Action N` name in that set. With `action`, it records into the action at `action` in set `set` instead
and returns `{set, action, name}`: `ui.action.stop` puts the recorded steps after step `after` of that action, or
after its last step when `after` is left out, and the steps after them keep whether they are excluded. The Actions
panel's Begin Recording button runs this command with the selected action and its selected step, and with neither
when no action is selected. `action` without `set`, `after` without `action`, an index past the last set, action or
step, an invalid set or an existing recording is an error and leaves the sets and recording unchanged, and so is a
set where the new action
would give it more than 1,000 actions or an action set file larger than 8 MiB. While a recording (this one or
`ui.action.new`'s) is in progress, a recorded command whose step would give the action more than 10,000 steps, or
its set's action set file more than 8 MiB, is an error with the reason (`the action already has 10,000 steps`, `the
action set would be larger than 8 MiB`): it does not run, and the recording goes on. A batch (`command.batch`) is
such a command too. A command whose step grows past a limit only as it runs, from values it adds to its step,
keeps its edit and result; the status bar names the limit, later recorded commands do not run, and
`ui.action.stop` leaves its step out. A stop that playback reaches
`ui.action.stop` leaves its step out. So does a `script.run` whose commands, or a tool's or panel's edit whose
step, takes the recording past a limit: the status bar names the limit when the run or the edit ends, and
`ui.action.stop` keeps the steps that fit. A stop that playback reaches
then is not recorded, and the status bar shows the reason. `ui.action.stop` leaves out any recorded steps still past
either limit and shows which one in the status bar.
- Actions: `ui.action.options {set, action, name?, key?, cmd?, shift?, color?}` changes the action at
@@ -3253,6 +3334,20 @@ already walks Image Trace. Short agent entry points:
they are excluded, and no step of that action stays selected. A Record Stop dialog open on that step closes, and one
open on a later step of the action follows it up. A missing or invalid index is an error, and then nothing changes.
Undo does not restore a deleted step. The panel's Delete Selection runs this for the selected step.
- Actions: `ui.action.moveStep {set, action, step, toSet?, toAction?, before}` moves one step, a stop or a command, of
the action at `action` in set `set` into the action at `toAction` in set `toSet` (by default the same set and
action), before the step at index `before` there, or after its last step when `before` is its number of steps.
`before` counts the steps as they are before the move. It returns `{set, action, step, moved}` with the step's new
place. The step keeps its params and whether it is excluded, and the other steps keep theirs. A selected step stays
selected; a moved selected step makes its new action the selected one and its set the current one. A Record Stop
dialog open on a step follows the step, and the steps that the recording in progress adds to either action stay
after the steps that were before them. A step put back just before or just after itself does not move, and
`moved` is `false`. A missing or invalid index, or a move that would give the action more than 10,000 steps or its
set's action set file more than 8 MiB, counting the steps that the recording in progress adds to the set, is an
error, and then nothing changes. Undo does not move the step back. In the Actions panel, dragging a step row moves
the step: a line shows where it goes, before or after the row under the pointer, or first in an action whose name
row it is over (after the last step when the action's steps are hidden), and releasing the button off those rows
leaves the step where it was.
- Actions: `ui.action.playbackOptions {speed?, seconds?}` sets how the Actions panel plays actions and returns
`{speed, seconds}`. `speed` is `accelerated` (the default: every step at once), `step` (one step at a time, with the
window drawn after each step before the next runs) or `pause` (a pause of `seconds` between steps). `seconds` is a
@@ -3261,8 +3356,8 @@ already walks Image Trace. Short agent entry points:
preferences. The panel menu's Playback Options… opens the `actionPlayback` dialog (`speed`, `seconds`), whose OK runs
this. Batch… (`action.batch`) plays every step at once whatever the setting.
- Actions: `ui.action.stop {}` ends the recording in progress and keeps its steps, in the action `ui.action.new` made
for it, else in a new action after the last action of its set, and returns `{stopped}`. It also ends an action
playing at Step by Step or Pause For, or one waiting at a stop, whose message box closes: the steps already played
for it or the action `ui.action.record` named, at the place it named, else in a new action after the last action of
its set, and returns `{stopped}`. It also ends an action playing at Step by Step or Pause For, or one waiting at a stop, whose message box closes: the steps already played
stay, and the rest do not play. With nothing
recording or playing it changes nothing. The panel's Stop Playing/Recording button runs this.
- Action sets: `ui.actionSet.delete {sets?: [index], ids?: [id]}` deletes the Actions panel's sets at these indexes
@@ -3338,7 +3433,9 @@ already walks Image Trace. Short agent entry points:
nothing when it touches none. `selectFills` off leaves the faces out, and `selectStrokes` off leaves the edges out. A click and a marquee run their command on the
release, and Escape between the press and the release removes the marquee so that the release runs nothing. The
tool does not highlight or select the faces and edges of a Live Paint group that is locked, on a locked layer or on
a locked artboard, or in isolation mode outside the isolated art.
a locked artboard, or in isolation mode outside the isolated art. In the app, the tool's cursor shows what a click
at the pointer selects under these rules: the arrow with a filled square for a face, with a short line for an edge,
and with an x for nothing. It keeps the look it had at the press until the release.
The Live Paint Bucket's options (`tool.setOption {tool: "livePaintBucket", values}`; double-clicking the tool opens
them, dialog `livePaintBucketOptions`) are `paintFills` (on), `paintStrokes` (off), `highlight` (on), `color` (the
highlight color, `#rrggbb` or a layer color's name; `#ff2a2a`) and `width` (the face highlight's width, 1 to 20 screen
@@ -3612,12 +3709,16 @@ effect of each selected object, in one undo step: `face` is `front` (default) or
offset in points of the graphic's center from the center of the object's bounds in the face's plane, `scale` is a
percentage of the symbol's size (1..10000; omitted, 100 or less so the graphic fits inside the object) and
`rotation` is in degrees counterclockwise (-360..360). Without `index` each object's first 3D effect takes it, and `item` is refused.
Every object's 3D effect must be Extrude & Bevel with Cap on (front only at a depth of 0) or Rotate (the Plane type,
front only), and `symbol` must name a document symbol; otherwise the command is refused and nothing changes. It returns `{ids, graphic}`.
Every object's 3D effect must be Extrude & Bevel with Cap on (front only at a depth of 0), Rotate (the Plane type,
front only) or Revolve (front only: its swept side), and `symbol` must name a document symbol; otherwise the command is refused and nothing changes. It returns `{ids, graphic}`.
On Revolve, `x` and `y` are measured on the side unrolled flat from its middle, and a graphic that does not fit
starts scaled to fit the half of the side seen from the front ([three-d.md](three-d.md#graphics)).
The graphics are the `graphics` parameter of `threeD.extrude` and `threeD.rotate`, which `effect.setParams` also
The graphics are the `graphics` parameter of `threeD.extrude`, `threeD.rotate` and `threeD.revolve`, which `effect.setParams` also
edits: an array of up to 64 `{symbol, face, x, y, scale, rotation}` objects, drawn on the face turned,
foreshortened and shaded with it and clipped to it ([Map artworks on 3D object](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/map-artwork-on-3d-objects.html)).
Revolve takes graphics on its swept side and shades them with the object's light ([the archived reference, page 185](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf#page=185));
how a graphic wraps around the side is VectorCraft's own rule.
A graphic naming a symbol the document does not have is refused, unless the effect already holds a graphic of it.
The drawing rules are VectorCraft's own; see [three-d.md](three-d.md#graphics).
@@ -3763,11 +3864,32 @@ with one gray channel, so a PDF writes it in gray.
`edit.colors.saturate` needs `intensity`, a number from -100 to 100 (a number outside the range counts as its nearest
end). Without one, or with `null`, it answers `` missing number `intensity` ``; with a value that is not a number, such as
`"40"`, it answers `` `intensity` must be a number, not "40" ``. Either way it changes nothing.
The `edit.colors.*` commands, `edit.colors.overprintBlack` and `addon.color.filter` answer a `fill`, `stroke`,
`includeImages`, `includePatterns`, `convert`, `remove`, `includeCmyBlacks` or `includeSpotBlacks` that is not true or
false with `` `fill` must be true or false ``, naming the key, a `percentage` that is not a number with
`` `percentage` must be a number ``, and a `mode` that is not a string with `` `mode` must be a string ``. They change
nothing and record no undo step. A null counts as a key left out.
The `edit.colors.*` commands other than `edit.colors.toIndexed`, `edit.colors.overprintBlack` and
`addon.color.filter` answer a `fill`, `stroke`, `includeImages`, `includePatterns`, `convert`, `remove`,
`includeCmyBlacks` or `includeSpotBlacks` that is not true or false with `` `fill` must be true or false ``, naming the
key, a `percentage` that is not a number with `` `percentage` must be a number ``, and a `mode` that is not a string
with `` `mode` must be a string ``. They change nothing and record no undo step. A null counts as a key left out.
`edit.colors.toIndexed` (Edit › Edit Colors › Convert to Indexed Color…) stores each embedded image in the selection (or
`ids`), in groups too, as an indexed-color PNG: a palette of at most 256 RGB colors, with each pixel the index of one.
`reduction` chooses the palette: `selective` (the default), `perceptual`, `adaptive`, `web`, `gray` and `blackWhite`
choose it as a PNG-8 export does; `uniform` takes evenly spaced levels of red, green and blue (2 to 6 levels, the most
whose cube fits in `colors` with one entry left for transparency); `document` takes the colors of the document's
swatches (at most 255). `colors` (2 to 256, default 256) is the most palette entries, the transparent one included;
`uniform` needs at least 9, and `blackWhite` and `document` refuse it. `dither` is `diffusion` (the default), `pattern`,
`noise` or `none`, and `ditherAmount` is 0 to 100 (default 100). Reduction and dither ids are read as a PNG-8 export
reads them, ignoring case. Pixels under half opacity become one transparent entry and the others become opaque in their
own color. Linked images, colorized image fills and the images of color glyphs in outlined type keep their pixels. Each
image that changes becomes a new image, in one undo step for all of them. An image that is already an indexed PNG and
would show the same pixels is left as it is, and no undo step is recorded when no image changes. `notChanged` counts the
locked and hidden objects Select All left out that hold an image that is not an indexed PNG yet. An image of more than
134,217,728 pixels (33,554,432 in the web app) is refused with an error, read from the file's header before any pixel is
decoded, and nothing changes. A value out of range or of the wrong type, such as `"colors": "8"`, is refused with an
error and changes nothing.
```json
{"name":"run_command","arguments":{"command":"edit.colors.toIndexed","params":{"reduction":"adaptive","colors":32,"dither":"pattern"}}}
```
The reply of `recolor.apply` and of every command above except the Blend commands has `notChanged: {locked, hidden}`.
When the selection is exactly what `select.all` takes (in isolation mode, what it takes inside the isolated group), it
@@ -4416,6 +4538,13 @@ and `layerRows`, the rows highlighted in the panel.
draws and is clicked in outline, Ctrl/Cmd-click its eye), `dimImages` (0–100, or false) and `color` (an index 0–26,
`#rrggbb` or a preset name; a hex color with an alpha part, `#rrggbbaa` or `#rgba`, is an error, and so it is in
`layer.new` and `layer.newSublayer`). Several rows change in one undo step (a drag down the eye or lock column).
A ruler guide's row has its own eye and lock: `ids` may name ruler guides (their `id` in `guide.list`), which take
`visible` and `locked`. They can be changed while the guide's layer is hidden or locked, and the guide shows
or can be edited only when its layer allows it as well. Alt-click on a guide row's eye or lock acts on the other
layers, as on any row in that layer. A click on a guide row selects the guide (`guide.select`) and clears the
highlighted rows (`layer.highlight {ids: [], mode: "set"}`), so Delete then runs `edit.clear` on the guide; with no
row highlighted, the trash button runs `edit.clear` on the selected guides and art, and guide rows dropped on the
trash are deleted (`guide.remove {indexes}`).
- **Dragging rows**: `layer.move {ids, target, place?: above|below|inside, copy?}` moves rows beside a row or into a
layer or group (on top of its contents), keeping their stacking order; `copy` (Alt-drag) moves copies. Layers go
only in layers or at the top level and objects in layers and groups (an object placed beside a top-level layer goes
@@ -4563,7 +4692,7 @@ paper white, 0 %) exports to PDF as a Separation shading, and one that mixes spo
inks exports as a DeviceN shading. A gradient that mixes a spot ink with colors written in RGB or Lab is written in
process colors, with a warning; an export that converts colors to a CMYK destination profile writes RGB and Lab stops
in CMYK, and so does a CMYK document unless the export converts colors to an RGB destination profile. Freeform gradients and dithered fills export to PDF as images in process colors, and Method
above covers Perceptual gradients.
above covers Perceptual gradients and gradients mixed around the hue wheel.
```json
{"name":"run_command","arguments":{"command":"paint.editGradient","params":{"stops":[{"offset":0,"swatch":"Ink"},{"offset":1,"swatch":"Ink","tint":20}]}}}
@@ -4636,6 +4765,14 @@ tip, has more than 65,536 anchors in its art or in the shapes it paints, has sha
or has more than 4,096 anchors once united is listed in `skipped`. `stroke.arrowheads.list` returns each head's `name`, `reach` and `halfWidth`, the `file`
path, the `skipped` symbols and any read `error`.
Under automation read roots (see the automation roots section above), the arrowheads file counts as a path the client
did not give. `stroke.arrowheads.list` returns `file` as `null` when the file is outside the read roots, and its
`error` names the file by its file name; the heads, `skipped` and the reason stay. `stroke.arrowheads.edit` and
`stroke.arrowheads.reload` refuse a file outside the read roots with the root-check error, naming the file by its file
name: `edit` then makes no folder or file and neither opens the file nor brings it to the front, and `reload` does not
read it. Inside the read roots, `edit` opens the file under the client's roots. The Stroke panel menu's Edit
Arrowheads… is not confined.
`stroke.set {endArrow: {"custom": name}}` puts a custom head on a stroke. The stroke keeps a copy of the head's art,
so the document draws it, saves it and exports it to SVG and PDF the same way without the arrowheads file.
`document.inspect` reports such a head as `{"Custom": {name, path}}`, which `stroke.set` takes back unchanged. It
@@ -4713,7 +4850,9 @@ and those of the object shape the outline of the clip group, the gradient fits t
box, as the canvas paints the fill; the expanded art does not keep them. With `fill: false` gradient and pattern fills
stay live. `object.expand.info` answers which options have something to expand in the selection
(`{object, fill, stroke, gradient, appearance, symbolArt?}`; `gradient` says whether Fill has a gradient fill, which Expand
Gradient To applies to; `appearance` whether the selection has appearance attributes Expand Appearance expands first,
Gradient To applies to, that it turns into art: it is false when every such fill repeats or reflects in more than 256
runs across the box it fills, which for type, symbol art included, is the box of each outline Object makes of a character,
ruby character, underline or strikethrough; `appearance` whether the selection has appearance attributes Expand Appearance expands first,
for which Object → Expand… is dimmed in the menus; `symbolArt: {fill, stroke, gradient}`, present when the selection
holds symbol instances that `object` expands, whether only their art holds it, which Fill and Stroke reach only with
`object` on), and `object.mesh.create` on a gradient-filled object colours the
@@ -4769,7 +4908,12 @@ files) and `eventNames` (the 19 events the panel lists, from `onfocusin` to `ons
event's script where they have one (an empty `script` removes it); `svgInteractivity.removeEvent {event, ids?}` and
`svgInteractivity.clearEvents {ids?}` remove one event or all of them. Each is one undo step.
`svgInteractivity.addFile {file}` and `svgInteractivity.removeFile {file}` link and unlink a JavaScript file (a path
or a URL, kept as given without surrounding spaces); a document links at most 64 files. `ui.javascriptFilesDialog {}`
or a URL, kept as given without surrounding spaces); a document links at most 64 files. `addFile` takes a file as
already linked only when the same text is linked. With automation read roots in force, `info`, `addFile` and
`removeFile` name a linked file outside them by its file name alone in `files` (a relative path is taken from the
document's folder, and a URL is kept), and `removeFile` also unlinks such a file by that name. When the name fits
more than one linked file (two such files, or such a file and a file linked as that name), `removeFile` returns an
error and unlinks nothing. `ui.javascriptFilesDialog {}`
opens the JavaScript Files dialog, whose Choose… puts a picked file in as a `file:///` URL. SVG export writes each event
as an event attribute on the object's element (`onclick="…"`; with the SVG Tiny 1.2 profile it leaves them out with a
warning) and each file as `<script xlink:href>`. SVG import reads the event attributes the panel lists and the
@@ -5033,9 +5177,10 @@ round, such as a rectangle, stays as it is. Closed paths stay closed and open pa
{"name":"run_command","arguments":{"command":"object.path.smooth","params":{"level":80}}}
```
In the desktop app, Object › Path › Smooth opens the slider (`ui.menuDialog {command: "object.path.smooth"}`, dialog
`smoothSlider` with field `level`) at the Auto-Smooth level and previews the result until `ui.dialog.confirm`;
`ui.smoothAuto` moves the open slider back to the Auto-Smooth level.
In the desktop app, Object › Path › Smooth and the canvas context menu's Smooth… (listed while the selection holds a
path) open the slider (`ui.menuDialog {command: "object.path.smooth"}`, dialog `smoothSlider` with field `level`) at
the Auto-Smooth level, and the slider previews the result until `ui.dialog.confirm`; `ui.smoothAuto` moves the open
slider back to the Auto-Smooth level.
## Editing a path with the Curvature tool
@@ -5511,11 +5656,12 @@ the selected artboards (those the Artboard tool has selected, else the active on
Artboards panel menu's Fit to Artwork Bounds also passes the selected artboards. A batch passes its `artboard` on to
each of their steps that names no `index`, as the step's `index`. When the document a step runs in has no artboard at
that index, the step gets that document's last artboard. An `artboard.fitToArt` step that names neither `index` nor
`indices`, while the Artboard tool is chosen with several artboards selected when the step runs, gets those artboards
as its `indices` instead, and the batch's journal entry records them, so a replay of the journal fits the same
artboards. A step's own `index` or `indices` is kept. An action recorded in the Actions panel leaves `index` and
`indices` out of these steps: when the action plays, Fit to Artwork Bounds resizes the artboards the Artboard tool then
has selected (two or more), else the active artboard, and Fit to Selected Art resizes the active artboard.
`indices`, while the Artboard tool is chosen with two or more artboards selected when the step runs, gets those
artboards as its `indices` instead. The batch's journal entry records the `indices` or `index` such a step got, so a
replay of the journal fits the same artboards. A step's own `index` or `indices` is kept. An action recorded in the
Actions panel leaves `index` and `indices` out of these steps: when the action plays, Fit to Artwork Bounds resizes the
artboards the Artboard tool then has selected (two or more), else the active artboard, and Fit to Selected Art resizes
the active artboard.
```json
{"name":"run_command","arguments":{"command":"object.align","params":{"horizontal":"left","to":"artboard","artboard":1}}}
@@ -5698,7 +5844,9 @@ From the Selection, Direct Selection, Group Selection, Magic Wand, Lasso or Pers
drawing tool chosen last in the window, or the Pencil tool when none has been chosen yet; from any other tool it
chooses the Selection tool, and the tool left becomes the drawing tool unless it is the Hand, Rotate View, Zoom or Print
Tiling tool. It returns `{tool}` and adds no undo step. Pencil as the first drawing tool and the list of selection tools
are VectorCraft's own rules. A tap in Presentation Mode runs nothing.
are VectorCraft's own rules. A tap in Presentation Mode runs nothing. Under VectorCraft's own rule, a tap on the canvas
while a modal dialog is open runs nothing, and fingers already down when it opens make no tap when they lift, during
the dialog or after it closes.
`gridlineEvery` (72 pt by default) and `gridSubdivisions` (8 by default) set the document grid that View › Show Grid
draws, and View › Snap to Grid snaps to its subdivisions. Changing either sets the grid of every open document.
@@ -6083,7 +6231,10 @@ document. With no document open they fail with "no document open". `document.act
the current one. `document.move {from?, to}` reorders open documents the way dragging a document tab along the tab strip
does: `from` defaults to the current document, `to` is the destination index, documents between them shift by one, the
current document stays current, and each document keeps its own view (#5663). It is an engine command, so headless and
remote `command_list` both list it.
remote `command_list` both list it. `file.close {index}` on a document other than the current one leaves the current
document current, and its tool keeps its state; closing the current document makes the document in the next tab
current, or the one before it when it was the last tab. In the desktop app a modified document comes to the front first,
to ask whether to save it.
`window.arrange.tile` (Window › Arrange › Tile) shows every open document in its own pane, edge to edge in the document
area, in a grid of as many columns as the square root of the number of documents, rounded up, and fits each document to
its pane; with fewer than two documents open it leaves the view and `tiled` as they are. It returns `{tiled, panes}`.
@@ -6186,6 +6337,9 @@ with the defaults.
them.
- `file.newProfiles.reset {name}` deletes the category's file, so the shipped profile applies again, and returns
`{name, reset}`. `reset` is false when there was no file. A copy of the profile that is open stays open.
- Under automation roots, `file.newProfiles.edit` needs write and read access to the profile's file and
`file.newProfiles.reset` needs write access. An error names the file whole only inside the read roots, and
otherwise by its file name.
The profile is read on every `file.new`, so a replayed journal or a recorded action uses the profile as it is when it
runs, as `document.open` reads its file when it runs. A file larger than 64 MiB (VectorCraft's own limit) is not read.
@@ -7045,6 +7199,10 @@ color, even one of the same color, and Registration are not spot colors; the spo
Color Objects and `objects.spots` counts them; spot swatches no art uses are not listed; with `selectionOnly`, those
in the selection), `spotColorNames` and `spotColors` (the spot colors those objects use) and `pathLength` (pt: the total length of the paths, compound paths' pieces included and guides left
out, which objects lists as Path Length in the document's units, say for a cut line or an LED strip along the letters);
`brushNames`, `brushes`, `spotObjects`, `patternObjects` and `gradientObjects` leave out hidden objects, objects at an
opacity of 0 and the objects inside a hidden group or layer or one at an opacity of 0, their characters' paints and
colorized images included, and so does `selectionOnly` for an object selected inside such a group or layer, while
`objects` still counts them as paths, groups and the other kinds;
`format: "text"` returns the plain-text report Document Info › Save… (`docInfo.save {path?, selectionOnly?, category?}`:
only `category` when given, else every category) and the package report write.
@@ -9938,8 +10096,9 @@ the Selection and Blend tools show the same values as widgets
16 px above and below the spine's halfway point (where the boxes of three or more other visible objects hold that
spot, a widget moves out in 16 px steps, up to 144 px, joined to the halfway point by a line): the upper one sets
`colorShift` and the lower one, shown only when the steps follow the spine as `ramp` needs, sets `ramp`, by one per screen pixel dragged along the spine and by one per arrow key
during the drag (ten with Shift). Each drag is one undo step, and `press_key` Escape or a switch to another tool
during it puts the value back:
during the drag (ten with Shift). A blend made without a spine shows the upper widget alone, placed against the
straight lines between its key centers that its steps are laid out along, which are not drawn. Each drag is one undo
step, and `press_key` Escape or a switch to another tool during it puts the value back:
```json
{"name":"run_command","arguments":{"command":"object.blend.setEasing","params":{"id":40,"ramp":35}}}
@@ -10057,11 +10216,26 @@ order along the path changes: Space, the start, Pivot, Rotate and the children's
new place along the path counted from 0. `object.objectsOnPath.selectMember {member}` takes the same kind of `member`
and returns `{group, member}`.
`object.objectsOnPath.detach {member?}` takes one object off the path: `member`, or without it the object chosen with
the Select/Move widget of the one selected group (otherwise it fails with "choose the object to detach with its
Select/Move widget"). `member` takes the same kind of object as `moveMember`, and neither it nor the group may be
hidden or locked. The object leaves the group and goes just above it in the group's layer or group, staying where it
is drawn, and becomes the selection; when the group is itself an object on another group's path, the object goes just
above that outer group, so it does not join its path. The objects after it move up the path in their order: with Auto Space they spread
evenly again (on an open path the first and last on its ends while two or more are left), and a Space value and the
start are kept, drawn as close as the path has room for. Hidden and locked objects left in the group move up too. The
group, its path and its options stay even with one object or none left, and an object moved back into it joins the
path. It is one undo step, and undo puts the object back on the path with the choice it had. It returns
`{group, member}`. In the desktop app, Detach in the Properties panel's Objects on Path Options section sends it, and
is dimmed until one group is selected with an object chosen. To take several objects off at once, drag their rows out
of the group in the Layers panel (`layer.move`).
```json
{"name":"run_command","arguments":{"command":"tool.setOption","params":{"tool":"objectsOnPath","values":{"pivot":7,"rotate":30}}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.attach","params":{"path":30,"ids":[12,15,18]}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.options","params":{"pivot":7,"rotate":15}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.options","params":{"spacing":24,"start":0}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.detach","params":{"member":15}}}
{"name":"run_command","arguments":{"command":"object.objectsOnPath.expand","params":{}}}
```
+6 -1
View File
@@ -617,7 +617,7 @@ An **importer** gets the bytes of the file being opened, as they are (not JSON),
`paint`, `width` in points, `join` (`"miter"`, `"round"` or `"bevel"`), `cap` (`"butt"`, `"round"` or `"square"`)
and `miterLimit`. Paints use the native file format's form: `{"type": "solid", "color", "swatch"?, "tint"?}`,
`{"type": "gradient", "gradient": {"kind": "Linear"|"Radial"|"Freeform", "stops": [{offset, color, opacity,
midpoint}], "dither"?, "method"?: "Perceptual"}, "geom"?, "angle", "freeform"?}`, `{"type": "pattern", "pattern",
midpoint}], "dither"?, "method"?: "Perceptual"|"HueClockwise"|"HueCounterclockwise"}, "geom"?, "angle", "freeform"?}`, `{"type": "pattern", "pattern",
"xf"?}`. Colours are `{"model": "rgb", r, g, b}`, `{"model": "cmyk", c, m, y, k}`, `{"model": "gray", k}` (ink: 0
white, 1 black) or `{"model": "lab", l, a, b}`; components 0–1 except Lab's.
- `effects` (filters): the object's own effects in order, as its effect records: `id` (as `effect.list` lists it, or
@@ -779,6 +779,11 @@ is a piece drawn by itself, in the order returned, with paints of its own:
starts at 1 pt, miter join, butt cap, miter limit 10.
- An effect on a fill (`_context.item` `{"fill": i}`) returns only `fills`, which replace that fill on each piece; an
effect on a stroke returns only `strokes`. The object's other fills and strokes stay where they are.
- Each fill or stroke such an effect returns keeps the effects after it in that fill's or stroke's stack. A later
live effect, and a later geometry effect on strokes, runs once on each of them in paint order: it is given the
piece's `fills` and `strokes` with `_context.item` naming that one (`{"fill": 1}` for the second fill on the
piece), and its result replaces only that fill or stroke. A run that fails, is refused, is deferred or would pass
the budget leaves its own fill or stroke as it was, and the others still run.
- The effects after it in the stack apply to the pieces together, the reference box being the bounds of all of them.
Bounds, raster effects, Pathfinder effects, compound shapes and Envelope Options › Distort Appearance use the pieces
as they are drawn. Outside outline mode, an image is also hit where the pieces of its own fills and strokes paint; a
File diff suppressed because one or more lines are too long
+40 -15
View File
@@ -320,8 +320,9 @@ Our reading:
The public [Map artworks on 3D object](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/map-artwork-on-3d-objects.html)
page describes graphics: art kept with the document and placed on single surfaces of a 3D object, and the
[3D and Materials panel options](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/3d-materials-panel-options.html)
page lists Graphics and Add Materials and Graphics on the Materials tab. VectorCraft maps graphics onto planar faces:
Extrude & Bevel's front and back caps, and the plane of the Plane type (Rotate).
page lists Graphics and Add Materials and Graphics on the Materials tab. VectorCraft maps graphics onto
Extrude & Bevel's front and back caps, the plane of the Plane type (Rotate), and the swept side of Revolve, which the
public page's example of a label on a bottle describes.
- The panel's **Materials** tab shows the document's graphics as thumbnails. A graphic is a document symbol, as
the public page allows existing symbols to be used. **Add as Single Graphic** keeps the selected art as a new
@@ -330,7 +331,8 @@ Extrude & Bevel's front and back caps, and the plane of the Plane type (Rotate).
- Clicking a thumbnail maps that graphic onto the front of the 3D effect of each selected object
(`threeD.mapGraphic`), in one undo step. A graphic larger than the object starts scaled down to fit inside it.
- Below the thumbnails the tab lists the graphics mapped onto the first selected object. The selected one shows
its Face (Front or Back; Plane has only a front), X and Y (the offset of its center from the center of the
its Face (Front or Back; Plane and Revolve show no Face, as a plane has only a front and Revolve's graphics go
on its side), X and Y (the offset of its center from the center of the
object's bounds, in the face's plane), Scale (a percentage of the symbol's size) and Rotation
(counterclockwise), and **Remove Graphic** takes it off. Each change is one undo step.
- While the tab is open, a blue handle sits on the center of each mapped graphic whose face is turned toward the
@@ -349,6 +351,26 @@ These rules are VectorCraft's own:
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:
the angle from the middle of the side times the profile's distance from the axis at that point, so a graphic
keeps its size at every distance and wraps farther around the axis where the profile comes nearer to it. Y runs
along the profile, toward its lower end, measured from the middle of its length; a profile's edges on the axis
sweep nothing and add no length. On a closed profile that does not touch the axis, Y is measured from where its
outer side crosses the middle of its height, and the graphic is cut on its inner side. The middle of the side
is the middle of the part of the sweep that faces the viewer before the object is turned, which is the part at
90° with the axis on the left and at 270° with it on the right (the middle of the sweep when no part faces the
viewer). On a compound profile the graphics go on its longest contour. Each face
of the side draws the part of the graphic that falls on it, shaded as the face is shaded, so the graphic narrows
toward the silhouette and is not drawn where the side's outside faces away or past the ends of the sweep; nearer
faces drawn after it cover it. A full turn cuts the graphic at the back, on the edge between two segments nearest
half a turn from the middle, and the end caps take no graphic; a part
that runs past the end of the profile reaches the curved rim of the cap and stops there. On a group or layer the
graphics go once, on the side of the member with the longest profile. Curves
are cut into straight pieces within 0.05 pt before they are mapped, and an opaque part takes a 0.3 pt stroke of
its paint over the seams between faces; a part filled with a pattern, or whose gradient has a transparent stop
or point, takes none. A graphic larger than the half of the side seen from the front starts
scaled to fit it. The panel shows no handle for a Revolve's graphics; X and Y move them. Expand Appearance keeps
every face of a surface with graphics.
- The symbol's fills and strokes are drawn as filled areas, a stroke as its outline, and their gradients and
patterns are placed with the graphic. Images, hidden objects,
guides and clipping paths in the symbol are left out, and clipping groups draw their art unclipped. Instances
@@ -357,12 +379,15 @@ These rules are VectorCraft's own:
- Without perspective, the graphic maps exactly. With perspective, straight lines stay straight and each curve is
cut into 8 cubic pieces before it is mapped. The part of a graphic at or behind the eye is left out.
- An object takes up to 64 graphics. One graphic draws up to 4,096 fills and strokes, and the graphics of one 3D
effect draw up to 400,000 path elements together. Commands refuse a graphic past either limit. A loaded file
effect draw up to 400,000 path elements together (on Revolve, 400,000 points after the curves are cut, and up to
2,000,000 once they are cut at the faces, with up to 64,000,000 points cut together, and the copies of their
paints in the parts take up to 256 MiB per object, apart from those of the surfaces). Commands refuse a graphic
past either limit. A loaded file
draws the object without a graphic past the first limit, and without all of its graphics past the second.
- A hollow Extrude & Bevel has no caps, so its graphics are not drawn until Cap is turned on again, and
`threeD.mapGraphic` refuses to map a new one onto it. Changing the type to Revolve or Inflate
drops the graphics, and changing between Extrude and Plane keeps them, except that Plane drops the graphics
on the back cap.
`threeD.mapGraphic` refuses to map a new one onto it. Changing the type to Inflate drops the graphics, and
changing between Extrude, Plane and Revolve keeps them, except that Plane and Revolve drop the graphics on the
back cap; on Revolve the front's graphics go on its side.
- `effect.apply` and `effect.setParams` refuse a graphic that names a symbol the document does not have, unless
the effect already holds a graphic of that symbol. A graphic whose symbol is deleted is no longer drawn. Renaming a symbol renames it in the graphics that use it,
in the layers, in the art of symbols, patterns, graph designs, brushes and opacity masks, and in graphic
@@ -376,9 +401,9 @@ These rules are VectorCraft's own:
uses: `rotationX`, `rotationY`, `rotationZ`, `perspectiveAngle` (degrees), `shade`, `lightColor` (`#rrggbb`, default `#ffffff`), `lightIntensity` (%), `lightRotation`,
`lightHeight` (degrees), `lightSoftness` (%), `ambient` (%, to 200), `lightBehind`. Lengths are points and scale with the object (Scale Strokes & Effects).
Commands validate the shape and the parameters before the document changes. All four also take `lights`: null (the default, use the earlier single-light fields), or an array of one to ten objects. Each object takes `color` (opaque `#rrggbb`, white), `intensity` (0..100%, 80), `rotation` (-180..180 degrees, 125.26), `height` (0..90 degrees, 30), `softness` (0..100%, 0), and `behind` (boolean, false). Omitted settings use those defaults. An explicit list replaces the earlier single light; `ambient` and `shade` remain shared. Set `lights` to null to return to the single-light fields. Invalid lists, counts, keys and values are rejected before editing; malformed loaded lists fall back to the earlier light safely. All four take `shadow` (boolean, false), `shadowPosition` (`behind` or `below`), `shadowDistance` (0..100%, 0) and `shadowBounds` (10..400%, 100) ([Shadows](#shadows)); values out of range are rejected before editing.
`threeD.extrude` and `threeD.rotate` take `graphics` ([Graphics](#graphics)): an array of up to 64 objects, each
with `symbol` (the name of a document symbol), `face` (`front` or `back`, default `front`; `threeD.rotate` takes
`front` only), `x` and `y` (points, default 0), `scale` (1..10000%, default 100) and `rotation` (-360..360 degrees,
`threeD.extrude`, `threeD.rotate` and `threeD.revolve` take `graphics` ([Graphics](#graphics)): an array of up to 64 objects, each
with `symbol` (the name of a document symbol), `face` (`front` or `back`, default `front`; `threeD.rotate` and
`threeD.revolve` take `front` only), `x` and `y` (points, default 0), `scale` (1..10000%, default 100) and `rotation` (-360..360 degrees,
default 0). Other keys, values out of range and graphics on the other 3D effects are rejected before editing.
## What differs from the public documentation
@@ -388,11 +413,11 @@ and [Create 3D objects](https://helpx.adobe.com/illustrator/desktop/special-effe
pages, and the archived Extrude & Bevel option list (depth 0–2,000, cap on/off, bevel height, bevel extent in or
out, perspective). No Adobe software, assets, presets or bevel shapes were used.
- **Materials** (base and Adobe Substance), graphics on curved surfaces (Revolve, Inflate and Extrude & Bevel's
walls and bevels), soft shadows, shadows of lights after the first, and **ray-traced rendering** are not built:
shading uses directional lights, in the paint of the fill or stroke the solid is built for
([Fills and strokes](#fills-and-strokes)), and the shadow is hard-edged ([Shadows](#shadows)). Graphics map
onto planar faces only ([Graphics](#graphics)).
- **Materials** (base and Adobe Substance), graphics on Inflate and on Extrude & Bevel's walls and bevels, soft
shadows, shadows of lights after the first, and **ray-traced rendering** are not built: shading uses directional
lights, in the paint of the fill or stroke the solid is built for ([Fills and strokes](#fills-and-strokes)), and
the shadow is hard-edged ([Shadows](#shadows)). Graphics map onto planar faces and Revolve's side only
([Graphics](#graphics)).
- The bevel shapes and their names are our own; custom bevel paths are not supported.
- Visibility is a painter's algorithm: faces of a closed solid turned away are left out, and the rest are drawn
back to front, with a flat cap or flat back turned toward the viewer drawn after all of them. Concave solids
+3 -3
View File
@@ -250,7 +250,7 @@ tools, never checked side by side) isn't captured row by row.
| A double-click on a ruler adds a guide at the pointer: a vertical one on the top ruler, a horizontal one on the left ruler (swapped in a view turned 90°, where the rulers read the other document axis; the nearer axis in between), exactly where the pointer is (no snapping), shown even with guides hidden, one undo step (`guide.add`); both clicks of the pair must be on that ruler, and the first adds nothing. The [public page on rulers](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html) doesn't describe this gesture, so it follows the request in #5239 | done | #5239; an artboard guide with the Artboard tool, as a drag from a ruler makes one; `canvas/tests.rs` `double_clicking_a_ruler_adds_a_guide_at_that_position`, `a_canvas_click_then_a_ruler_click_adds_no_guide`, `a_ruler_double_click_follows_a_rotated_view` |
| Escape cancels an existing ruler-guide press, move or copy | done | Selection and Direct/Group Selection; app-input regressions cover preselected guides, document/interaction-start selection, Undo/Redo and saved state, later on-canvas motion/release and temporary borrowing from idle Pen. All eight checks and the Direct Selection Escape control passed in current full CI. [Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area) |
| Ruler origin: corner drag with preview, snapped as a drawn point is (Smart Guides, Snap to Point and the ruler guides, pixels, the grid), double-click reset to the active artboard, relative X/Y fields, native save/reopen | done | #1212; `document.setRulerOrigin`, `document.resetRulerOrigin`; geometry and command coordinates stay absolute |
| Ruler guides as Layers rows: shown, hidden, locked and deleted with their layer; rows dragged to another layer | done | `guide.setLayer`; guides from older files go on a layer |
| Ruler guides as Layers rows: shown, hidden, locked and deleted with their layer; rows dragged to another layer; each row's own eye and lock; a row dropped on the trash deletes its guide | done | `guide.setLayer`; guides from older files go on a layer; `layer.setProps` on a guide's id hides or locks it alone; Object › Lock/Hide › Selection, Unlock All and Show All reach guides; `guide.remove {indexes}` |
| Objects made into guides: drawn in the guide colour and style of Preferences; Lock Guides locks them too (no select, move or delete); a dragged selection snaps to ruler guides with Smart Guides on, by the nearest point of the path when pressed on its stroke or beside its edge, so the path lands exactly on the guide | done | #1534 (#1543, #1583); #3701 |
| A selected ruler guide's X or Y in the Transform panel; typing a value moves it | not scored | #1659 (part of #1597); several guides move together; not in Properties or the Control bar. Not established by the documentation we could find: the help pages on guides, rulers and the Transform, Control and Properties panels (read again on 2026-10-14) don't describe it, and the one public statement is a user's [community answer](https://community.adobe.com/questions-652/adding-a-new-custom-guide-785586), which is not documentation. Kept as a convenience pending the owner's decision; no parity claim |
| Alt swaps a ruler guide's orientation while dragging | missing | Alt-drag copies the selected guides ([Use rulers](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html)); `guide.move` `{copy: true}` |
@@ -416,7 +416,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Actions panel: Delete Selection (bottom bar) deletes the selected set with its actions and keeps the other sets ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html)) | partial | `ui.actionSet.delete`; `panels/actions.rs` tests. VectorCraft's own rules, which the overview does not cover: deleting a set asks first (a selected action is still deleted at once), Shift-click on a set's name selects a range of sets, ≡ › Delete does the same as the button, and Default Actions and the last set can be deleted. Undo does not restore a deleted set, and recorded commands cannot be selected or deleted |
| Actions panel: ≡ › Duplicate copies the selected action or set ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html)) | partial | `ui.actionSet.duplicate`; `panels/actions.rs` tests. VectorCraft's own rules, which the overview does not cover: the copy goes right after the original, is named after it with " copy" added and is selected, and a copied set holds copies of all its actions. Undo does not remove the copy, and recorded commands cannot be duplicated |
| Properties panel per context (every selection type's Quick Actions) | partial | one clip group: Edit Clipping Path and Edit Contents, at the top of the panel rather than in Quick Actions (G1); the overview's additional controls ([Properties panel overview](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/properties-panel-overview.html)): Blend Options (one blend selected, with any tool, #4817), Repeat Options (one radial, grid or mirror repeat selected: Object › Repeat › Options, #5181; the overview does not list it) and Merge Live Paint (a Live Paint group with more) in Quick Actions, the Width Profile list (Width tool or a variable-width stroke) and an applied effect's Delete icon in Appearance, Corner and Corner Radius for corners selected with Direct Selection; Variable Font was already there; Edit Artboards: a Transform section (reference point, X, Y, W, H, preset, orientation) above the Artboards section (#5219) |
| Select › Start Global Edit / Stop Global Edit; Properties › Quick Actions with the Global Edit Options popover (Match: Appearance, Size; On Artboards: All Artboards or a range, Include Objects On Canvas; Result(s)); Escape stops it; the matched objects are highlighted in their layer colour while it runs; a Shift-click on a highlighted match leaves it out | partial | #3521, `select.globalEdit` / `select.globalEditOptions` / `select.globalEditExclude`, `crates/engine/src/tests/globaledit.rs`; paths, compound paths, symbol instances and groups of them (similar groups, #4939: edits inside one group reach the same object in the others; [public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-groups-with-similar-objects.html)); Shift-click to leave a match out ([public source](https://helpx.adobe.com/ph_fil/illustrator/how-to/global-editing-sensei.html)); the Control bar's Global Edit Options dropdown opens the same options; rotated copies and highlighting matches before the start remain ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-similar-objects.html)) |
| Select › Start Global Edit / Stop Global Edit; Properties › Quick Actions with the Global Edit Options popover (Match: Appearance, Size; On Artboards: All Artboards or a range, Include Objects On Canvas; Result(s)); Escape stops it; the matched objects are highlighted in their layer colour while it runs; a Shift-click on a highlighted match leaves it out | partial | #3521, `select.globalEdit` / `select.globalEditOptions` / `select.globalEditExclude`, `crates/engine/src/tests/globaledit.rs`; paths, compound paths, symbol instances and groups of them (similar groups, #4939: edits inside one group reach the same object in the others; [public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-groups-with-similar-objects.html)); a group holding type or an image is not taken, as Global Edit does not take images or text; Shift-click to leave a match out ([public source](https://helpx.adobe.com/ph_fil/illustrator/how-to/global-editing-sensei.html)); the Control bar's Global Edit Options dropdown opens the same options; the matches are highlighted before the start (`selecting_one_object_highlights_the_similar_objects_before_global_edit_starts`); rotated copies remain ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-similar-objects.html)) |
| Contextual task bar | done | |
| Control bar with Transform and Stroke popovers | done | |
| Stroke panel: custom arrowheads after the built-in ones in both arrowhead menus, from the arrowheads file that the panel menu's Edit Arrowheads… opens; saving the file reloads the menus, and a stroke keeps a copy of its custom head's art | done | [Apply stroke on an object](https://helpx.adobe.com/illustrator/using/stroke-object.html) (Customize arrowheads); the file's layout (one symbol per head, pointing right, tip at the registration point, height drawn as wide as the built-in heads) is VectorCraft's own; `stroke.arrowheads.edit`, `stroke.arrowheads.list`, `stroke.arrowheads.reload`, `stroke.set` `{custom: name}`; engine `tests::arrowheads`, `stroke::tests_arrows::custom_heads_draw_their_own_art_at_the_tip` |
@@ -437,7 +437,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| Canvas context menu | done | Layers rows, panels and type have none (gap) |
| SVG Interactivity panel: JavaScript for each object's events and the document's linked JavaScript files, written to SVG and read back as text | done | `svgInteractivity.*`, `ui.javascriptFilesDialog` |
| Retype, Comments, Version History panels | missing | the last two are cloud features (out of scope) |
| Blend panel (30.7) | partial | Window › Blend provides Type (Ease In, Ease Out, Ease In Out, Ease Out In), Ramp 0–100% and independent color Shift, alongside the existing blend controls. Easing is disabled without a spine. Original polynomial distributions approximate the publicly documented behavior; on-canvas easing handles remain missing. [Public source](https://helpx.adobe.com/illustrator/desktop/manage-colors/apply-transparency-and-blending/blend-panel-overview.html) |
| Blend panel (30.7) | partial | Window › Blend provides Type (Ease In, Ease Out, Ease In Out, Ease Out In), Ramp 0–100% and independent color Shift, alongside the existing blend controls. Easing is disabled without a spine. Original polynomial distributions approximate the publicly documented behavior. On-canvas Appearance and Easing widgets set Shift and Ramp; a blend without a spine shows the Appearance widget alone. [Public source](https://helpx.adobe.com/illustrator/desktop/manage-colors/apply-transparency-and-blending/blend-panel-overview.html) |
| Gradient panel: Dither (30.0) | done | Dither box under the stops (`paint.editGradient` `dither`); dimmed behind the Stroke proxy, fills only; `panels::gradient` test |
| Gradient panel: Method, Classic or Perceptual (30.0) | done | Method menu under the Stroke buttons for linear and radial gradients (`paint.editGradient` `method`); [public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-gradients/control-perceptual-interpolation-in-gradients.html); `panels::gradient` test |
| Gradient panel: Presets (30.0) | done | Presets menu by library (theme) plus Saved: applies a preset to the gradient behind the active proxy, keeping its placement, one undo step; panel menu Save Gradient Preset…, Delete Gradient Preset, Hide/Show Presets (`gradient.presets.*`); `panels::gradient` tests |