mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 12:24:19 +00:00
Merge main into feat/rotated-box-scale-reflect
This commit is contained in:
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
File diff suppressed because one or more lines are too long
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+67
-22
File diff suppressed because one or more lines are too long
+40
-15
@@ -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
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user