mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 09:39:52 +00:00
Merge remote-tracking branch 'upstream/main' into fix/remaining-type-envelopes
# Conflicts: # docs/file-format-parity.md # docs/target-app-parity.md
This commit is contained in:
@@ -111,7 +111,7 @@ the two can still win that race.
|
||||
| `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 with Use Artboards |
|
||||
| `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). `ui.dialog.confirm` writes `path` (else asks for one), returns `{path, bytes, warnings}` and opens the file when `viewAfterSaving` is set |
|
||||
| `file.save` / `file.saveAs` / `file.saveCopy` / `file.saveAsTemplate` (via `engine.execute`) | `{path?, format?, options?, svg?}` | Save writes the document's own file in its own format (a document opened from or saved as SVG/PDF saves as that again; the result's `warnings` say what the format loses). With `path` the file is written at once (Save As or a Copy to an SVG path without SVG options first opens SVG Options, below). Without one (never saved, a converted older file, Save As, a Copy, a Template) the app shows a save panel with one file type per save format (`vectorcraft`, `template`, `pdf`, `svg`, `svgz`, `ai`; for a Template, `template` and `ait`), then the format's options: SVG opens the `svgOptions` dialog (below; returns `{"pending": "svgOptions", "path"}`), PDF the `savePdf` dialog (its `__save` field names the save command); `ui.dialog.confirm` finishes the save. A name typed in the panel without the file type's extension gets it, and a name the panel returns with the extension of a format the panel does not list has it replaced (in a native save, `card.png` and `card.ait` are saved as `card.vectorcraft`, and `notes.v2`, whose extension names no format, is saved as `notes.v2.vectorcraft`); when a file of that name exists, which the panel did not ask about, the save (and every export or other write through a save panel) answers `cancelled` and opens a `replaceFile` dialog (fields `message`, `detail`, `path`): `ui.dialog.confirm` writes that file, `ui.dialog.cancel` writes nothing. On the web a `saveOptions` dialog (fields `path`, `format`) first names the file and picks the format. `file.formatOptions {format?}` reads the options a save would use. `file.revert` opens a `confirm` dialog (`{"pending": "confirm"}`); `ui.dialog.confirm` reloads the saved file in the same tab |
|
||||
| `file.save` / `file.saveAs` / `file.saveCopy` / `file.saveAsTemplate` (via `engine.execute`) | `{path?, format?, options?, svg?}` | Save writes the document's own file in its own format (a document opened from or saved as SVG/PDF saves as that again; the result's `warnings` say what the format loses). With `path` the file is written at once (Save As or a Copy to an SVG path without SVG options first opens SVG Options, below). Without one (never saved, a converted older file, Save As, a Copy, a Template) the app shows a save panel with one file type per save format (`vectorcraft`, `template`, `pdf`, `svg`, `svgz`, `ai`; for a Template, `template` and `ait`), then the format's options: SVG opens the `svgOptions` dialog (below; returns `{"pending": "svgOptions", "path"}`), PDF the `savePdf` dialog (its `__save` field names the save command); `ui.dialog.confirm` finishes the save. A name typed in the panel without the file type's extension gets it, and a name the panel returns with the extension of a format the panel does not list has it replaced (in a native save, `card.png` and `card.ait` are saved as `card.vectorcraft`, and `notes.v2`, whose extension names no format, is saved as `notes.v2.vectorcraft`); when a file of that name exists, which the panel did not ask about, the save (and every export or other write through a save panel) answers `cancelled` and opens a `replaceFile` dialog (fields `message`, `detail`, `path`): `ui.dialog.confirm` writes that file, `ui.dialog.cancel` writes nothing. A save or export that writes other files beside that path (a file per artboard, linked images, image maps) checks them before it writes anything: when any of them exists, it writes nothing and opens a `replaceFiles` dialog (fields `message`, `detail` (each file's name when there are several), `files`, `__command`, `__params`; the reply is `{"pending": "replaceFiles"}`, or comes once a Background Save or Export ends): `ui.dialog.confirm` runs the save or export again and replaces them, `ui.dialog.cancel` writes nothing; a dialog open when a background job asks stays open, and the question follows it. A save does not ask about the files the document's last Save or Save As in this session wrote. On the web a `saveOptions` dialog (fields `path`, `format`) first names the file and picks the format. `file.formatOptions {format?}` reads the options a save would use. `file.revert` opens a `confirm` dialog (`{"pending": "confirm"}`); `ui.dialog.confirm` reloads the saved file in the same tab |
|
||||
| SVG Options (`engine.execute` of `file.export.svg`, `file.saveAs`, `file.saveCopy`) | `{}` / `{path?, svg?: {…}}` | `file.export.svg` without params, and Save As or Save a Copy to an `.svg`/`.svgz` path without SVG options, open the `svgOptions` dialog (`ui.dialog.set` its fields: `styling`, `outlineText`, `images`, `objectIds`, `decimals`, `minify`, `responsive`, `fewerTspans`, `metadata`, `preserveEditing`, `useArtboards`, `allArtboards`, `range`, `showCode`, `profile` (`svg11`/`tiny12`), `encoding` (`utf8`/`utf16`/`latin1`), `embedFonts`; `ui.dialog.confirm` exports or saves and remembers the choices). With `svg: {…}` (the `document.formats` SVG options) they run directly. `app.save {path?, svg?}` takes the same options; a document saved as SVG saves as SVG again with the same options |
|
||||
| `engine.execute` `file.place` | `{paths?}` / `{path \| name+dataBase64, …file.place params}` | with no file, picks files and opens the `place` dialog; with `paths`, opens it for them (fields `files`, `link`, `template`, `replace`; `__replace` says whether Replace applies, `__info` describes each file). `ui.dialog.confirm` places one file centered in the view (or in place of the selected object) and loads the place cursor with several (`file.place.queue`). In the desktop app the dialog reads its files off the UI thread: `__info` is empty until they are read, `ui.dialog.confirm` returns `{background: true, status}`, `ui.inspect` lists the files still being read in `placing`, and the file is placed (or the cursor loaded) once it is read. With a file, it runs `file.place` centered in the view, at once. Files dropped on the window with no document open open. On the web, files dropped on the canvas are placed at the drop point (linked; Shift embeds) and files dropped off it (the tab bar) open, as in Illustrator; desktop drops carry no pointer position, so documents (native, SVG, PDF, `.ai`, EPS, DXF, EMF/WMF) open as tabs of their own and pictures and text are placed in the middle of the view. Swatch libraries and color books (`.vcswatches`, `.gpl`, `.ase`, `.acb`), graphic style libraries (`.vcstyles`), Libraries panel files (`.vclibrary`), presets (`.vcflattener`, `.vcpdfpresets`, `.vcprintpresets`, `.vcperspective`, `.vcexportpresets`), plug-ins (`.wasm`) and shortcut sets (`.kys`, in Import Shortcut Set) open wherever they are dropped, as File › Open opens them. With the cursor loaded (`ui.inspect` → `tool: "place"`, `toolOptions: {count, current, name, names}`), `ui.pointer` clicks and drags place files (in the desktop app each file is read off the UI thread and placed once it is read, and `ui.inspect` lists it in `placing` until then) and `ui.key` ←/→/↑/↓ and Escape cycle and discard them |
|
||||
| `app.export` raster options | `{path?, format: png \| jpg \| webp, ppi?, width?, height?, background?, antiAlias?, interlaced?, quality?}` | `ppi` sets the pixel size (72 = one pixel per point) and is stored in PNG and JPEG files (WebP files store no resolution); `width` or `height` sets the size of each file in pixels instead (`width` wins), and PNG and JPEG files store the resolution that results; `background` is `transparent`, `white`, `black` or `"#rrggbb"`; `antiAlias` is `none`, `art` or `type` (text snapped to pixels); `interlaced` writes an Adam7 PNG. The PNG Options dialog (`pngOptions`; `jpgOptions` / `webpOptions` for the other raster formats) has the same fields: fill them with `ui.dialog.set` and press OK with `ui.dialog.confirm` |
|
||||
@@ -807,6 +807,9 @@ EMF and WMF: Export As… lists both formats (`file.exportAs {format: "emf"}` or
|
||||
dialog, so `ui.dialog.confirm` asks for the path and writes the file (one per artboard with Use Artboards). Open and
|
||||
Place read `.emf` and `.wmf`; a paste of `image/emf` from a system clipboard service runs `clipboard.importEmf`.
|
||||
|
||||
HP-GL: Export As… lists it (`file.exportAs {format: "hpgl"}`); it has no options dialog, so `ui.dialog.confirm` asks
|
||||
for the path and writes the `.plt` file with the default options (one per artboard with Use Artboards).
|
||||
|
||||
TIFF, BMP and Targa Options: Export As… with the TIFF, BMP or Targa format (`file.exportAs {format: "tiff"}`, then
|
||||
the save dialog) opens `tiffOptions`, `bmpOptions` or `tgaOptions`: the raster rows of PNG Options (`ppi`,
|
||||
`background`, `antiAlias`), then TIFF's `colorModel` (`rgb`, `cmyk`, `gray`; a CMYK document starts on `cmyk`),
|
||||
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
# Development
|
||||
|
||||
> **Last updated:** 2026-11-06 · **Change:** Hindi catalog and zero/one plural rule; desktop Devanagari fallback and optional craft-fonts UI face; native review pending.
|
||||
> **Last updated:** 2026-11-12 · **Change:** minor (clipboard failure helper follows its Linux/BSD and test callers; previously: Hindi catalog and zero/one plural rule; desktop Devanagari fallback and optional craft-fonts UI face; native review pending).
|
||||
> **Last updated:** 2026-11-01 · **Change:** Linux: Automatic Linux Display Server opens the window through XWayland when a pen is attached on a Wayland session, so the pen draws (#4967); `VECTORCRAFT_NATIVE_WAYLAND=1` keeps Wayland.
|
||||
> **Last updated:** 2026-10-31 · **Change:** files opened while the app runs open in its window on Windows and Linux (#4892); `--new-instance`.
|
||||
> **Last updated:** 2026-10-29 · **Change:** Linux clipboard: the Flatpak on GNOME copies and pastes text through the core Wayland protocol.
|
||||
@@ -351,6 +351,10 @@ parameters. Shipped code therefore never panics. Anything that can fail returns
|
||||
|
||||
### Enforced by lints
|
||||
|
||||
The clipboard timeout failure helper is compiled for Linux/BSD workers and unit tests; production
|
||||
macOS reads synchronously. Keep helper target gates aligned with their callers instead of disabling
|
||||
dead-code warnings across the workspace.
|
||||
|
||||
`Cargo.toml` denies these clippy lints for the whole workspace, and `cargo xtask ci` runs clippy with
|
||||
`-D warnings`. The gate targets the current stable Rust (`rust-version` in `Cargo.toml` is only the
|
||||
minimum that builds the app, and no toolchain is pinned): when a new stable adds lints, they are fixed
|
||||
@@ -508,6 +512,7 @@ Scores and hour estimates are unchanged.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-12 | minor | Compile the clipboard failure helper only for Linux/BSD worker callers and unit tests; keep production macOS free of the unused helper without suppressing workspace dead-code warnings. |
|
||||
| 2026-11-06 | minor | Localisation: Hindi catalog, zero/one plurals, desktop and web font requirements; AI/MT draft, native review pending. |
|
||||
| 2026-10-31 | minor | Files opened while the app runs open in its window on Windows and Linux (#4892); `--new-instance` opts out. |
|
||||
| 2026-10-29 | minor | Linux clipboard: core Wayland protocol text fallback where arboard has neither data-control nor X11 (the Flatpak on GNOME).; its reads wait 2 s at most. |
|
||||
|
||||
@@ -28,7 +28,7 @@ Part of [target-app-parity.md](target-app-parity.md); open work is in [gaps.md](
|
||||
| Rasterize | Rasterize… (as a live effect) | 1/1 | — |
|
||||
| Stylize | Drop Shadow, Feather, Inner Glow, Outer Glow, Round Corners, Scribble | 6/6 | Scribble Options' Settings presets |
|
||||
| SVG Filters | Apply SVG Filter… and the filter list | 1/1 | The submenu lists the document's own filters: VectorCraft ships no default filters. A filter that is the object's last effect is written to SVG as a `<filter>` element; otherwise SVG export draws the object as an image. A filter on a turned or skewed object in an imported SVG is left out with a warning |
|
||||
| Warp | Arc, Arc Lower, Arc Upper, Arch, Bulge, Shell Lower, Shell Upper, Flag, Wave, Fish, Rise, Fisheye, Inflate, Squeeze, Twist | 15/15 | warp point editing in envelopes; Arc is a circular arc in the object's own proportions (#1578) |
|
||||
| Warp | Arc, Arc Lower, Arc Upper, Arch, Bulge, Shell Lower, Shell Upper, Flag, Wave, Fish, Rise, Fisheye, Inflate, Squeeze, Twist | 15/15 | warp point editing in envelopes; each style bends the bounds as one Bézier patch (`version` 1), so Arc's edges are cubic curves close to concentric arcs; warps saved without a version keep the earlier map, where Arc is a circular arc in the object's own proportions (#1578) |
|
||||
|
||||
Document Raster Effects Settings exists. When the 64 million pixel cap on an effect image lowers
|
||||
its resolution, PDF export warns once per object with the requested and the lowest achieved ppi
|
||||
@@ -181,13 +181,13 @@ 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 ([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)); no materials, 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), Rotate (Classic) and Map Art: none |
|
||||
| 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)) |
|
||||
|
||||
The `three-d` crate (depends on `geom` and `pathops`, with no UI or GPU dependencies) builds, rotates, projects, shades and depth-sorts the surfaces;
|
||||
the effects crate turns them into art. What's left (materials and shadows, ray-traced rendering,
|
||||
angular cut caps, exact visibility, the rest of 3D Classic (Revolve, Rotate, Map Art), Turntable) is 25–45 h.
|
||||
angular cut caps, exact visibility, the rest of 3D Classic (Map Art), Turntable) is 25–45 h.
|
||||
|
||||
## Revision history
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# File format parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (`.ai` and EPS import: type in an envelope with Distort Appearance is also tried mapped from the box of what it paints, its strokes' miter tips and strokes a Transform effect moves included, after its glyphs grown by half its strokes' weight, #6824; `.ai` and EPS import, and what every export and `.ai` save draw: type's strokes in an envelope paint at their own weight along its bent outlines, Distort Appearance on or off, #6644; `.ai` and EPS import: centred and right-aligned lines of tracked type are aligned without the tracking after their last character, as the file's page aligns them, #6610; `.ai` and EPS import: an envelope saved in Edit Contents whose mesh lies away from its drawn art by a move opens live on that art, #6570; `.ai` save and open: layer colour presets Yellow, Magenta and Cyan keep their colour, as the `Lb` colour index now follows the file's numbering, #6502; `.ai` save: stroked type keeps its stroke's cap, join and miter limit in the text document, #6472; `.ai` and EPS import, PDF import: tracking and manual kerning in horizontally scaled type take the Horizontal Scale, as the file's page spaces such type; `.ai` and EPS import: stroked type in an envelope maps from its glyphs grown by half its stroke, #3179; `.ai` and EPS import: warps of type open as mesh envelopes on their saved grid, #3179; `.ai` and EPS import: stroked type keeps its stroke's cap, join and miter limit from the text document, #6472; `.ai` save: type in a variable font's named instance names its design-space setting, #6388; `.ai` and EPS import: envelopes of type open live where they draw as their drawn art and the layers with them draw as the page, #3179; `.ai` and EPS import: a note for type in an envelope, #3179; `.ai` and EPS import: the live envelope and warped type check sees white art; PDF import: DeviceN images of one ink or of process plates keep their inks, #6348; `.ai` import: images in a spot ink's Separation space keep their ink, #6350; PDF import: Separation images keep their ink, #6314; PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; PDF import: image masks open colorized by the color they are painted in, #6313; EPS and `.ai` editing data: a ligature the page names by a private-use glyph matches its text object; EPS import: CIDFonts only named, over a Unicode CMap, set as live type; EPS import: CMap mappings to base-font codes and glyph names; EPS import: CID-keyed fonts in FontSets (CFF) draw their glyphs; EPS import: `kshow` procedures kern live type; EPS import: embedded Type 1 fonts' glyphs and widths; EPS import: CID-keyed fonts draw their glyphs; EPS import: Type 42 fonts' characters and widths from the font they carry, show operators' spacing kept as tracking; EPS import: an image decoder's own buffers stop counting against the memory budget once it is decoded; EPS import: sampled functions interpolate between their samples; `.ai` and EPS type from the text document keeps its tab stops; it keeps auto leading and hyphenation options, and may show more of the file's last lines in an area frame; `.ai` and EPS import: Art brushes in the older form of their objects come into the Brushes panel; EPS import: gradients drawn as abutting strips come in as one gradient, without seams; EPS import: composite fonts draw their Type 42 glyphs; SVG import: filter and mask regions clip, #5876, #5875; SVG import: Gaussian blurs keep `color-interpolation-filters`, #5877; PDF and `.ai` export: objects' URLs as links, #5785; SVG import: `color-interpolation="linearRGB"` gradients, #5878; `.ai` import: paths with a Calligraphic brush open brushed, #372) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-11 · **Change:** minor (`.ai` and EPS import: type in an envelope with Distort Appearance is also tried mapped from the box of what it paints, its strokes' miter tips and strokes a Transform effect moves included, after its glyphs grown by half its strokes' weight, #6824; newly imported SVG/SVGZ titles are filtered and bounded without changing restored titles, #3626; `.ai` and EPS import, and what every export and `.ai` save draw: type's strokes in an envelope paint at their own weight along its bent outlines, Distort Appearance on or off, #6644; `.ai` and EPS import: centred and right-aligned lines of tracked type are aligned without the tracking after their last character, as the file's page aligns them, #6610; `.ai` and EPS import: an envelope saved in Edit Contents whose mesh lies away from its drawn art by a move opens live on that art, #6570; `.ai` save and open: layer colour presets Yellow, Magenta and Cyan keep their colour, as the `Lb` colour index now follows the file's numbering, #6502; `.ai` save: stroked type keeps its stroke's cap, join and miter limit in the text document, #6472; `.ai` and EPS import, PDF import: tracking and manual kerning in horizontally scaled type take the Horizontal Scale, as the file's page spaces such type; `.ai` and EPS import: stroked type in an envelope maps from its glyphs grown by half its stroke, #3179; `.ai` and EPS import: warps of type open as mesh envelopes on their saved grid, #3179; `.ai` and EPS import: stroked type keeps its stroke's cap, join and miter limit from the text document, #6472; `.ai` save: type in a variable font's named instance names its design-space setting, #6388; `.ai` and EPS import: envelopes of type open live where they draw as their drawn art and the layers with them draw as the page, #3179; `.ai` and EPS import: a note for type in an envelope, #3179; `.ai` and EPS import: the live envelope and warped type check sees white art; PDF import: DeviceN images of one ink or of process plates keep their inks, #6348; `.ai` import: images in a spot ink's Separation space keep their ink, #6350; PDF import: Separation images keep their ink, #6314; PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; PDF import: image masks open colorized by the color they are painted in, #6313; EPS and `.ai` editing data: a ligature the page names by a private-use glyph matches its text object; EPS import: CIDFonts only named, over a Unicode CMap, set as live type; EPS import: CMap mappings to base-font codes and glyph names; EPS import: CID-keyed fonts in FontSets (CFF) draw their glyphs; EPS import: `kshow` procedures kern live type; EPS import: embedded Type 1 fonts' glyphs and widths; EPS import: CID-keyed fonts draw their glyphs; EPS import: Type 42 fonts' characters and widths from the font they carry, show operators' spacing kept as tracking; EPS import: an image decoder's own buffers stop counting against the memory budget once it is decoded; EPS import: sampled functions interpolate between their samples; `.ai` and EPS type from the text document keeps its tab stops; it keeps auto leading and hyphenation options, and may show more of the file's last lines in an area frame; `.ai` and EPS import: Art brushes in the older form of their objects come into the Brushes panel; EPS import: gradients drawn as abutting strips come in as one gradient, without seams; EPS import: composite fonts draw their Type 42 glyphs; SVG import: filter and mask regions clip, #5876, #5875; SVG import: Gaussian blurs keep `color-interpolation-filters`, #5877; PDF and `.ai` export: objects' URLs as links, #5785; SVG import: `color-interpolation="linearRGB"` gradients, #5878; `.ai` import: paths with a Calligraphic brush open brushed, #372) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
Every format Illustrator opens, places, saves or exports, against VectorCraft's format table (`FORMATS`,
|
||||
`OPEN_EXTS`, `PLACE_EXTS` in `crates/engine/src/cmd/fileio/formats.rs`). Illustrator's list comes from its public
|
||||
@@ -155,7 +155,8 @@ private editing data. VectorCraft:
|
||||
under them, clipped to where they show, along a smooth outline traced at the image's own resolution; that art stays) or omitted (Transparent Images,
|
||||
`transparentImages: "flatten"|"omit"`). Type is written as editable text objects (point type, area type and type
|
||||
on a path, with their fonts' PostScript names, sizes, colours, alignment, indents, leading, tracking and
|
||||
scaling), which the reading app sets in those fonts; characters outside Windows-1252, vertical type, ruby, inline
|
||||
scaling, and the Standard Vertical Roman Alignment and Tate-chu-yoko marks as `Tv` and `TY`), which the reading
|
||||
app sets in those fonts; characters outside Windows-1252, vertical type, ruby, inline
|
||||
graphics and gradient or pattern fills make that type outlines, and so do rotated characters (Character Rotation,
|
||||
which a version 7 text object can't hold: the outlines keep the rotation, and the type is no longer editable text
|
||||
in the file), and Text: Outlines (`outlineText: true`) writes
|
||||
@@ -291,7 +292,7 @@ Names in the editing data that aren't UTF-8 (layers, objects, artboards, colors,
|
||||
| PostScript `.ps` | open, place | yes (the EPS reader reads its first page; `.epsf` files open and place as EPS) | via Print to PostScript | `tests_epsimport.rs` |
|
||||
| SVG / SVGZ | open, place, save | yes | yes (SVG Options, Preserve Editing, symbols, filters, rich text) | strongest format; tests in `crates/svg/tests`; text on a path follows basic shapes as their SVG 2 equivalent paths (#1527); CSS transforms and the `font` shorthand (#1565), percentage lengths in symbols (#1596), Inkscape flowed text as area type (#1568) and Inkscape pages as artboards (#1601) import; an unsupported filter is named in its warning (#1598); an artboard exported on its own (Export As with Use Artboards, Export for Screens, Asset Export) has the artboard's name as `<title>`, Save the document's (#3544); attributes in other applications' XML namespaces (`plotter:pen="2"`) kept on their objects from import to export, as `data-*` attributes are (`crates/svg/tests/it/foreign_attributes.rs`); simple shapes export as `<rect>`, `<circle>`, `<ellipse>`, `<line>`, `<polygon>` and `<polyline>` where they draw the same as the path (#4652, `crates/svg/tests/it/basic_shapes.rs`); the regions of exported masks and filters (opacity masks, inverted masks, knockout groups) cover the art they apply to, and over type keep a region that covers any artwork (#6401, `crates/svg/tests/it/blend_export.rs`) |
|
||||
| DXF | open, export | yes | yes (R12–2018) | R13–2018 closed SPLINE flags and control-point closure on import verified (#1299); circular cubic paths export as native CIRCLE/ARC where stroke semantics permit; R12 weighted strokes remain POLYLINE while non-circular curves keep the existing fallback (`crates/cad/src/tests.rs`); [Autodesk group 70](https://help.autodesk.com/cloudhelp/2024/ENU/AutoCAD-DXF/files/GUID-E1F884F8-AA90-4864-A215-3182D47A9C74.htm). DXF 2000–2018 LAYOUT objects write the paper image origin as 148/149 (was a stray 158), the shade plot codes (2004+) and the model type flag, and the plot style placeholder carries no subclass marker (#3085; structure test `r2000_and_later_files_have_the_structure_cad_apps_require`, checked with ezdxf's audit; not yet confirmed in Autodesk Fusion). DXF Options' Preset menu and `document.exportDxf`'s `dxfPreset: "cnc"` set the CNC / CAM options (#5500); VCarve QA remains open |
|
||||
| HP-GL/2 `.plt`, `.hpgl`, `.hgl`, `.hpg` | — | yes (pen moves, pens and widths, line types and attributes, circles, arcs, wedges and Bézier curves, filled and edged rectangles, wedges and polygons, encoded polylines, scaling and rotation; labels, symbol mode, raster images and hatching aren't drawn) | no | recognized by its first instructions after any printer escapes, under its own extensions or one no other format reads; `crates/cad/src/hpgl/tests.rs`, `tests_hpgl.rs`, `import_fuzz.rs` |
|
||||
| HP-GL/2 `.plt`, `.hpgl`, `.hgl`, `.hpg` | — | yes (pen moves, pens and widths, line types and attributes, circles, arcs, wedges and Bézier curves, filled and edged rectangles, wedges and polygons, encoded polylines, scaling and rotation; labels, symbol mode, raster images and hatching aren't drawn) | yes (`.plt`: each visible path once along its line, a pen per color or a pen table, type as outlines, clipping applied, HP-GL or HP-GL/2 with pen colors and encoded polylines) | recognized by its first instructions after any printer escapes, under its own extensions or one no other format reads; `crates/cad/src/hpgl/tests.rs`, `crates/cad/src/hpgl/write/tests.rs` (plots read back by the reader), `tests_hpgl.rs`, `import_fuzz.rs` |
|
||||
| DWG | open, export | no | no | no open spec; DXF instead |
|
||||
| EMF / WMF | open, export | yes | yes | an oversized embedded PNG or JPEG is refused from its header, before any pixels are decoded (#1776) |
|
||||
| PSD | open, place, export | yes (open: layers as layers, or the merged image with `layers: false`; place: merged image; every mode and depth; `.psd` and `.pdd`) | yes (layered, max editability) | placing as layers and a PSD import options dialog missing; the embedded ICC profile's tags are 4-byte aligned (#1584) |
|
||||
@@ -341,6 +342,9 @@ Stop opacity on translated artboards remains outside this regression coverage.
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-12 | minor | `.ai` and EPS import: type in an envelope with Distort Appearance is also tried mapped from the box of what it paints, after the ways it was tried before (its glyphs alone and grown by half its strokes' weight, #6535, on its own warp and on its saved grid), before a grid moved onto its drawn art, and only where that box is another: its glyph outlines with their fills and strokes baked as the envelope bakes them (geometry effects on them applied: a stroke a Transform effect moves counts where it is moved to), each piece measured by its visual bounds, which are exact for miter joins (a tip counts where the miter limit keeps it, not the miter allowance at every corner that the bounds with strokes take) and round joins and an upper bound for bevel joins, miters the limit cuts, width profiles and square-capped dashes; a contour of zero length, which paints nothing, doesn't count. Effects on the type object itself are left out, as VectorCraft applies them after the envelope bends the type. The box is measured only when that try comes and the import has a check left for it, and once for an envelope. Groups round type and other objects in the contents are measured as before. In users' files (used locally, never committed) the drawn art of some stroked type in an envelope maps from that box: where the type has sharp corners at its edges (slanted stems, pointed feet) and miter joins, or a stroke moved by a Transform effect (a drop shadow), its glyphs grown by half the weight gave too small a box and the envelope kept its drawn art; and where a glyph holds a contour of zero length at its edge, the glyphs' box counted it. On 224 of our own files: envelopes of type that open live go from 154 in 45 files to 161 in 50, and no file loses one; no file changes whether it opens from its layers or its PDF part; renders at 40 ppi change in 6 files, by at most 0.94% of their pixels, each where an envelope of type now opens live in place of its drawn art (the page's own art): 1 closer to the file's own page and 5 further; the notes change in the same 6 files: the note on type in an envelope goes where none keeps its drawn art, and a file whose envelopes of type all kept their drawn art, which opened without the page comparison, now opens with it and carries its notes (the share of art or type that differs from the page, type the page has without a text object, the PDF part's note on DeviceN colours). A synthetic file of 256 such envelopes of 2,000 characters each opens in the same time as before (13.3 s in a debug build): the box is measured only when its try comes. Tests `ai::envelope::tests::mitred_type_is_also_tried_mapped_from_what_its_stroke_paints_miter_tips_included`, `type_with_a_mitred_character_stroke_is_also_tried_mapped_from_what_it_paints`, `a_stroke_moved_by_a_transform_effect_counts_where_it_paints`, `round_joined_type_is_not_tried_twice_for_the_same_box`, `a_contour_of_zero_length_paints_nothing`, `a_side_within_the_slack_of_the_geometric_box_is_that_side`, `tests_native::a_warp_of_mitred_type_maps_from_what_its_stroke_paints_where_its_drawn_art_does` (#6824, part of #3179) |
|
||||
| 2026-11-11 | minor | Newly imported SVG/SVGZ titles use the imported-text rules: generated Untitled names and control-only titles give the filename fallback, other control characters are removed, and titles are limited to 65,536 characters. Native and Preserve Editing titles remain unchanged (#3626). |
|
||||
| 2026-11-11 | minor | Native and Preserve Editing restores keep an existing File Info title exactly, including repeated spaces; imported-text fallback titles alone are normalized (#3626). |
|
||||
| 2026-11-03 | minor | An imported document's own title goes into File Info only (#3626): an SVG's root `<title>`, a PDF's, `.ai`'s or `.ait`'s XMP `dc:title` else its Info `Title` is File Info's title (`metadata.title` in the native format, left out when unset so other documents save unchanged and older versions ignore it; written by exports); the document's name, which the tab, Save As and the default export names use, stays the file's name. |
|
||||
| 2026-11-11 | minor | `.ai` and EPS import, and what the canvas, every export and `.ai` save draw: type's strokes in an envelope (its characters' and those on its own appearance) paint along the bent glyph outlines at their own weight, Distort Appearance on or off, as the drawn art of users' `.ai` files strokes them (used locally, never committed: there the drawn art keeps those strokes as stroked paths at the type's own weights, and its ring is as wide wherever the grid squeezes or stretches the type). With Distort Appearance, VectorCraft bent them with the art, as their filled outlines, so an envelope of stroked type in a squeezed or stretched grid didn't draw as its drawn art and kept it. The same holds in an envelope inside an envelope. Paths' strokes, geometry effects, opacity masks, linear gradients and pattern fills bend as before. On 224 of our own files: envelopes of type that open live go from 135 in 37 files to 154 in 45, and no file loses one; no file changes whether it opens from its layers or its PDF part; renders at 40 ppi change in 31 files, by at most 2.3% of their pixels, 19 closer to the file's own page and 12 further, 11 of those where an envelope of type now opens live in place of its drawn art (the page's own art) and one by 4 pixels. Tests `envelope_distort::type_strokes_paint_at_their_own_weight_in_a_squeezed_envelope`, `a_stroke_on_types_appearance_paints_at_its_own_weight_in_a_squeezed_envelope`, `a_paths_stroke_still_bends_in_a_squeezed_envelope`, `tests_native::an_envelope_of_stroked_type_whose_drawn_art_strokes_the_bent_outlines_at_the_weight_opens_live` (#6644) |
|
||||
| 2026-11-11 | minor | `.ai` and EPS import, and what SVG export, PDF export and `.ai` save draw: centred and right-aligned lines of tracked type are aligned without the tracking after their last character, as a `.ai`'s own page aligns them (see type-parity.md, same date). In 224 users' files (used locally, never committed), 63 render differently: 54 closer to the page, 7 further, 1 alike and 1 that couldn't be compared; among them the median share of pixels that differ from the page went from 4.4% to 1.0%. Of the 7: one now opens from its layers instead of its PDF part (they differ from the page by less than the import's 5% limit now; type note 3.8%); one has a centred line whose spacing inside it differs from the page's, which now centres on the page's line while half of it no longer lies on the page's glyphs (type note 1.4% to 2.4%); five moved by less than 0.05 of a percentage point, in type the page draws in other glyphs. The type note shows a smaller share in 6 files; 2 more envelopes of type open live (129 to 131, none fewer in any file). SVG import: `text-anchor` aligns a line's advance, which ends after its last character's letter spacing, so a centred or right-aligned letter-spaced line moves back by half or all of it and stays where a reader draws it; SVG export, which anchors lines so, reads back unchanged (test `import_text::anchored_letter_spaced_lines_stay_where_a_reader_draws_them`). Affinity import: centred or right-aligned tracked artistic text moves by half or all of its last character's tracking, as layout now aligns it (unverified against Affinity, whose files we don't have), and differently aligned paragraphs line up by the widths layout aligns them by (`affinity_features::artistic_text_aligns_its_lines_within_the_longest`). Scores and estimates unchanged. (#6610) |
|
||||
| 2026-11-11 | minor | `.ai` and EPS import: while an envelope's contents are edited, the editing data keeps its mesh (a mesh's, a top object's or a warp's grid) in its dictionary, and in some files that mesh lies away from the drawn art by a move, the contents with the drawn art, as if the mesh hadn't followed the envelope when it moved: the envelope drew its contents elsewhere and kept its drawn art. A mesh or top object saved in Edit Contents (or a warp of type tried on its saved grid) whose mesh draws the contents as large as the drawn art, within half a point over width and height together, but further from it than that, is now tried again, after every other way and with one more of the import's checks, with its mesh moved by as far as the drawn art's box lies from the box of what it draws; it opens live there where it then draws as the drawn art. In users' files, used locally and never committed, only envelopes saved in Edit Contents draw elsewhere than their drawn art, and nothing in their dictionaries says by how much they moved. Tests `ai::tests::an_envelope_in_edit_contents_whose_mesh_stayed_behind_opens_live_on_its_drawn_art`, `tests_native::a_warp_of_type_in_edit_contents_whose_grid_stayed_behind_opens_on_its_drawn_art` (#6570) |
|
||||
|
||||
+5
-4
@@ -630,8 +630,8 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi
|
||||
libraries ([public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/create-new-brush-libraries.html),
|
||||
[import](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/import-brushes-from-brush-libraries-or-other-files.html)):
|
||||
Window › Brush Libraries and the Brushes panel's Brush Libraries Menu open each library in a library panel of
|
||||
its own (six original libraries generated in code: Art Ribbons, Calligraphic Nibs, Pattern Borders, Scatter
|
||||
Shapes, Game Trails and Energy Beams; User Defined; Other Library… reads the brushes of any document VectorCraft opens); a
|
||||
its own (seven original libraries generated in code: Art Ribbons, Calligraphic Nibs, Bristle Brushes (one
|
||||
bristle brush for each tip shape), Pattern Borders, Scatter Shapes, Game Trails and Energy Beams; User Defined; Other Library… reads the brushes of any document VectorCraft opens); a
|
||||
click adds a
|
||||
brush to the document's Brushes with the patterns, symbols and other resources its art uses, as one undo
|
||||
step (applied to the selected paths in the same step), dragging one onto a path adds and applies it and
|
||||
@@ -652,7 +652,7 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi
|
||||
Fine Grain, Canvas Weave, Burlap Sack, Cracked Glaze, Mosaic Floor, Quilted Squares, Leaded Glass and Damp Paper,
|
||||
painted with the texture effects; User Defined; Other Library…; `graphicStyle.libraries`,
|
||||
`graphicStyle.addFromLibrary`).
|
||||
- **Missing:** more built-in brush libraries beyond the six shipped (and further graphic-style libraries beyond the ten shipped, and further symbol libraries beyond Basic
|
||||
- **Missing:** more built-in brush libraries beyond the seven shipped (and further graphic-style libraries beyond the ten shipped, and further symbol libraries beyond Basic
|
||||
Shapes, UI Frames, Map Markers, Inventory Icons and Skill Icons; original, generated in code, never Adobe's); dynamic symbols' `.ai` read and write;
|
||||
Global Edit of groups holding type or images, rotated copies as matches and highlighting matches before the
|
||||
start; 9-slice guides outside the symbol's art (a dragged guide stops at the art's edge).
|
||||
@@ -664,7 +664,7 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi
|
||||
Extrude & Bevel, Inflate and Rotate with Position presets ([three-d.md](three-d.md), #1407). Colored Inflate has automatic smooth vector lighting from shared membrane normals. White-light Inflate keeps its existing appearance. All four take one to ten colored directional lights, with neutral ambient applied once, a light list and independent previewing controls. Standard, Diffuse, Top Left and Right select original rigs, shared with commands; manual settings show Custom.
|
||||
- **Missing:** materials (base, Substance, graphics), shadows,
|
||||
ray-traced rendering, angular cut caps, exact visibility for intersecting and concave surfaces, the rest of 3D
|
||||
Classic (Revolve, Rotate, Map Art), Turntable, textures, map art and shading in 3D export.
|
||||
Classic (Map Art), Turntable, textures, map art and shading in 3D export.
|
||||
- **Impact:** specialist; Materials is the one menu stub left.
|
||||
- **Estimate:** 25–45 h; plan in `plan/` before coding.
|
||||
|
||||
@@ -858,6 +858,7 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-03 | minor | An imported document's own title (an SVG's root `<title>`, a PDF's XMP `dc:title` else its Info `Title`) goes into File Info only; the document's name stays the file's name (#3626); no scored gap or estimate reassessed. |
|
||||
| 2026-11-11 | minor | Eyedropper copies effective character dimensions between transformed text objects (#6404); scores and estimates unchanged. |
|
||||
| 2026-11-11 | minor | G5: built-in Energy Beams brush library (Laser Bolt, Plasma Arc, Charge Wave, Spark Trail); Missing trimmed; estimate 5–15 → 4–14 h; MCP Brush libraries list Energy Beams. |
|
||||
| 2026-11-11 | minor | G5: built-in Skill Icons symbol library (Attack, Defend, Heal, Buff, Ult); Missing trimmed; estimate 6–16 → 5–15 h; MCP Symbol libraries list Skill Icons. |
|
||||
|
||||
+321
-31
@@ -165,6 +165,14 @@ the suite) keep the agent to the folders you give it:
|
||||
and `modified` the document keeps of that file. Each row keeps its id, `name` (`fileName`), status and other
|
||||
fields. This applies to every document, also one the person or a client with wider roots opened. The paths a
|
||||
client gives `links.relink` and `links.unembed` stay in their replies, and the document's links are not changed.
|
||||
- `image.info` gives `link: null` for a linked file outside the read roots, as `links.info` leaves it out; its id,
|
||||
name, `linked` and the other fields stay. `links.findFiles` and `text.findFontFiles` replies, including later
|
||||
`{}` and `{stop: true}` calls on a search another client started, leave out the files found outside the read
|
||||
roots (with their `size` and `match`), a sought link's `path` outside them, and `folder` when the folder searched
|
||||
is outside them; `error` then gives the reason without the path (for example `the folder can't be read`). The
|
||||
rows keep their `name`, `ids`, family, style and status, and the counts stay. `text.addFontFiles` gives
|
||||
`folder: null` when VectorCraft's Fonts folder is outside the read roots, and only the file name in `copied` and
|
||||
`kept` `to` and in `skipped` `file` for a path outside them; the fonts are still copied.
|
||||
- `prefs.get`, `prefs.list`, `prefs.set` and `prefs.reset` show a folder preference outside the read roots
|
||||
(`fontsFolder`, `pluginsFolder`, `scratchPrimary`, `scratchSecondary`, `recoveryFolder`, `templatesFolder`) as
|
||||
`null`; an empty folder and the scratch disk choices `Startup` and `None` stay. In `prefs.get {}` and
|
||||
@@ -463,7 +471,14 @@ or a new object (`css.export`, `object.isolate`, `layer.locate`, `layer.releaseT
|
||||
counterparts, `image.info`, `links.info`, `links.goTo`, `links.unembed`, `shaper.release`, `shaper.expand`,
|
||||
`transparency.editOpacityMask`, `transparency.viewOpacityMask`, `symbol.edit`, the `object.blend.spine.*` commands
|
||||
and `path.curvature`) refuse an `id` that is not a non-negative integer (`"abc"`, `"5"`, `true`) with an error and
|
||||
change nothing; an omitted or null `id` keeps the default. A command that leaves out an object selected inside a
|
||||
change nothing; an omitted or null `id` keeps the default. Commands that need an object id (`document.node`, the
|
||||
`path.*` anchor and handle commands, `text.reshapePath`, `text.editRange`, the `object.mesh.*` point commands,
|
||||
`layer.target`, `node.move`, `select.magicWand` and the others like them, and the `source`, `target`, `group` and
|
||||
`path` keys of `appearance.transfer`, `appearance.copyFrom`, `layer.move`, the `livePaint.*` commands and
|
||||
`text.createInPath`) refuse a value that is not a non-negative integer with "`id` must be a whole number", naming the
|
||||
key, and report a key left out or null as missing. The optional `group` of `livePaint.fill` and `parent` of
|
||||
`node.move` are refused the same way when given; left out, they keep their defaults. `livePaint.info` reads `id` when
|
||||
`group` is left out or null, and reports a missing group only when both are left out. A command that leaves out an object selected inside a
|
||||
selected group also leaves it out when `ids` list it together with a group that holds it. Every command that takes
|
||||
`ids` (or `slices`, for the slice commands) refuses a list with an entry that is not a non-negative integer, or a value
|
||||
that is not a list, and changes nothing; it does not act on the other entries. The error for an entry names the key,
|
||||
@@ -527,7 +542,7 @@ applied colors still join the list while it is hidden.
|
||||
| `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`. |
|
||||
| `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`, 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. |
|
||||
| `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`, `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 and WMF 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. |
|
||||
| `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. |
|
||||
| `add_text` | `{text, x?, y?, width?, height?, fit?, fitMinPercent?, path?, mode?, pathEffect?, size?, font?, color?, justify?, vertical?}` | Point type at (x, y) (`justify`: `left`, `center` or `right` starts, centres or ends each line on x); area type with `width`/`height` (`fit`: `none`, `autoHeight`, `shrinkText`, as `text.areaOptions`); or `path` + `mode` (`area`/`onPath`) to flow text in or along a path, with `pathEffect` (`rainbow`, `skew`, `3dRibbon`, `stairStep`, `gravity`; SVG export writes type on a path with any effect other than `rainbow` as outlines, with a warning); `justify` (also `auto`, `justifyAll`) aligns the paragraphs of every kind, as `text.setStyle` does later. `vertical: true` uses the existing vertical writing mode for every kind (default `false`, horizontal). Returns `{id}`, with `overflow: true` when the text doesn't fit its frame or path. |
|
||||
| `apply_effect` | `{effect?, params?, ids?}` | Appends a live effect. Without `effect`, returns the effect catalogue with parameters and defaults. |
|
||||
| `pathfinder` | `{operation, ids?}` | `unite`, `minusFront`, `intersect`, `exclude`, `divide`, `trim`, `merge`, `crop`, `outline`, `minusBack`. For a live version, apply the `pathfinder.*` effect to a group (`effect.apply` groups several loose selected objects first). In remote desktop mode, `run_command` and `invoke_menu` can both call `pathfinder.repeat` with `{}`. Successful panel operations, direct `object.pathfinder.*` commands, and `command.batch` steps (including Actions panel playback) set its history. Repeat applies it to the current selection using current Pathfinder Options. Trap, compound-shape edits, live effects and `action.batch` do not set history. With no history it returns "no Pathfinder operation applied yet". |
|
||||
@@ -1873,8 +1888,13 @@ no engines, and the engines it left are dropped when their own thread next uses
|
||||
and the other eight are kept. Each named engine has a heap of 64 MiB, which holds what it keeps between runs; a run
|
||||
that goes past it ends with the heap limit, and the engine stays usable.
|
||||
- The documents go back after an uncaught error, a limit or `script.stop` as they do for any run, and the engine keeps
|
||||
the globals the run set before it ended. An internal error drops the engine. An error in a function an earlier run
|
||||
defined is reported without a line and column.
|
||||
the globals the run set before it ended. An internal error drops the engine.
|
||||
- An error in code an earlier run compiled, such as a function it defined, is reported at that code's line and column
|
||||
in the earlier run's script, a file it included or a file its `$.evalFile` ran, and the report's `file` names that
|
||||
file. For an earlier script given as `source`, `file` is that run's name. The report's `name` and the console line's
|
||||
name stay the calling run's, as in `caller.js: /path/lib.jsx:12:5: Error: kept`. Each engine keeps these places
|
||||
for 8 MiB of source maps, the oldest going first; an error in code whose place is gone, or in code that `eval` or
|
||||
`Function` compiled, is reported without a line and column, and the code still runs.
|
||||
- `vc`, `$`, `alert`, `confirm` and `prompt` act for the run that calls them, including a copy an earlier run kept in a
|
||||
global. `vc.args` is the calling run's `args`, through a `vc` object an earlier run kept too. An engine
|
||||
holds no document: `vc.document` is a copy, read when it is called. What a run sets on `$` (`$.locale`,
|
||||
@@ -2450,6 +2470,26 @@ kept with the workspace, and built-in workspaces and workspaces saved without it
|
||||
{"name":"invoke_menu","arguments":{"command":"window.toolsBesideCanvas","params":{"on":true}}}
|
||||
```
|
||||
|
||||
## Learn panel
|
||||
|
||||
Window › Learn and the Explore tutorials button beside the search icon at the right end of the app bar show and hide
|
||||
the Learn panel. Its For you tab holds a Get started section with a Quick skill guide card; choosing the card opens
|
||||
the Skills tab. The Skills tab lists VectorCraft's own skills, each with its number of tutorials and an empty progress
|
||||
ring. Choosing a skill lists its tutorials, choosing a tutorial shows its numbered steps, and Back goes up one level.
|
||||
The tutorial text is English. `window.learn {show?}` shows the panel (`show: true`), hides it (`show: false`) or
|
||||
toggles it (`show` omitted or null), and answers whether it shows; Window › Learn and Explore tutorials run it.
|
||||
The panel counts as hidden while the dock is hidden or a full screen mode hides the panels. Showing it shows a hidden
|
||||
dock; in a full screen mode it makes Learn the front panel, which shows when the mode ends, and answers false. Any
|
||||
other value of `show` is an error and changes nothing. `learn.list {}` → `{skills: [{id, title, tutorials: [{id, title, steps}]}]}`, in the order the Skills tab
|
||||
lists them, where `steps` is the number of steps. `learn.show {tutorial}` shows the panel on its Skills tab with that
|
||||
tutorial's steps → `{skill, tutorial, title, steps: [text]}`. A missing, non-string or unknown `tutorial` is an error
|
||||
and changes nothing. Behavior reference:
|
||||
[Quick skill guide](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html).
|
||||
|
||||
```json
|
||||
{"name":"invoke_menu","arguments":{"command":"learn.show","params":{"tutorial":"pen-paths"}}}
|
||||
```
|
||||
|
||||
## Ruler guides
|
||||
|
||||
Each ruler guide is an object of a layer or sublayer, directly in it and above its art, and takes an id from the
|
||||
@@ -2590,8 +2630,9 @@ already walks Image Trace. Short agent entry points:
|
||||
locked or sits in a hidden or locked group or layer. Choosing another selection tool selects the instance whole again.
|
||||
`symbol.instance.reset {ids?}` drops the selected instances' changes (the Properties
|
||||
panel's Reset). Redefining the symbol (`symbol.update`) reshapes every instance and keeps each change whose path the
|
||||
new art still has; `symbol.breakLink` bakes the changes into the expanded art; `symbol.edit` edits the symbol's own
|
||||
art, and each edit there keeps the changes whose path the art still has; making the symbol static drops them, and
|
||||
new art still has, less a fill or stroke the object now at that path does not draw (a change left empty is dropped);
|
||||
`symbol.breakLink` bakes the changes into the expanded art; `symbol.edit` edits the symbol's own art, and each edit
|
||||
there keeps the changes the same way; making the symbol static drops them, and
|
||||
so does `symbol.replace` with a different symbol (a replace with the instance's own symbol keeps them).
|
||||
|
||||
```json
|
||||
@@ -2964,6 +3005,21 @@ Tile, Start Tile or End Tile. A tile with a repeat frame has an unpainted rectan
|
||||
the frame. A symbol instance in the art is measured by its symbol's art. A calligraphic or bristle brush is refused
|
||||
with "`<name>` has no art (only scatter, art and pattern brushes do)".
|
||||
|
||||
`brush.replaceArt {name, ids?|id?, applyToStrokes?}` makes the selected art the brush's art and keeps its other
|
||||
options; several objects become one group, as in `brush.new`. A pattern brush takes a group named for a tile, or a
|
||||
group of such groups, tile by tile: an unpainted rectangle at the back of a tile group becomes that tile's repeat frame,
|
||||
a tile group's art goes back without that group, and the tiles that are not given stay as they are. Of two groups named
|
||||
for the same tile, the frontmost one counts. Other art becomes the side tile. When strokes use the brush and
|
||||
`applyToStrokes` is `false`, the brush and its strokes stay as they were, and a copy of the brush with the new art,
|
||||
named "`<name>` copy", goes after it and becomes the current brush. Otherwise, and for a brush no stroke uses, the brush
|
||||
changes and its strokes redraw. It replies `{name}`, the brush that has the new art, and refuses a calligraphic or
|
||||
bristle brush, an empty selection and art (or a tile it is given, such as a tile group left with only its frame's
|
||||
rectangle) with no width. `brush.replaceArt` and `brush.new` measure a symbol instance in the art by its symbol's art.
|
||||
In the app, art Alt-dragged (Option-dragged on macOS) from the canvas onto a scatter, art or pattern brush in the
|
||||
Brushes panel runs it. When strokes use the brush, the app checks the art first and shows why in the status bar when
|
||||
the brush cannot take it; otherwise a dialog (`brushReplaceArt`) asks: Apply to Strokes, Leave Strokes
|
||||
(`applyToStrokes: false`) or Cancel.
|
||||
|
||||
`variable.bind`, `variable.unbind`, `variable.delete`, `dataset.set`, `dataset.update`, `dataset.delete`,
|
||||
`dataset.select`, `dataset.next` and `dataset.prev` add no undo step when they leave the document as it was, such as
|
||||
binding art to a variable it already has or applying the active data set again: the steps to redo stay, and a saved
|
||||
@@ -3123,8 +3179,8 @@ See [Revolve](revolve.md) for the scope and limitations.
|
||||
replaces the lighting rig and ambient of an existing Revolve, Extrude & Bevel, Inflate or Rotate
|
||||
effect. `item: null` addresses object-level effects; an item index addresses a fill/stroke's
|
||||
effects. All targets must have a 3D effect at `index`, otherwise nothing changes. Extrude & Bevel
|
||||
(Classic) has no lighting presets: a target with it at `index` is an error, nothing changes, and
|
||||
`effect.setParams` sets its lights. The operation is one undo step and preserves Shading, geometry, source paint and the view.
|
||||
(Classic), Revolve (Classic) and Rotate (Classic) have no lighting presets: a target with any of them at `index` is an
|
||||
error, nothing changes, and `effect.setParams` sets its lights. The operation is one undo step and preserves Shading, geometry, source paint and the view.
|
||||
|
||||
The four original rigs use one white front light at 80% intensity. Standard keeps the existing
|
||||
default; Diffuse uses 100% Softness and 35% Ambient Light, Top Left uses Rotation 135 and Height
|
||||
@@ -3156,7 +3212,7 @@ art", and a loaded file draws the source path, as it does for a solid past its l
|
||||
documentation names the controls and their ranges
|
||||
([3D and Materials panel options](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/3d-materials-panel-options.html)).
|
||||
The settings are saved in the native file; SVG and PDF exports draw the shadow, and `effect.expandAppearance` keeps
|
||||
it as a path. Extrude & Bevel (Classic) has no shadow. See [three-d.md](three-d.md#shadows).
|
||||
it as a path. Extrude & Bevel (Classic), Revolve (Classic) and Rotate (Classic) have no shadow. See [three-d.md](three-d.md#shadows).
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"effect.setParams","params":{"index":0,"item":null,"params":{"shadow":true,"shadowPosition":"below","shadowDistance":10,"shadowBounds":150}}}}
|
||||
@@ -3232,6 +3288,51 @@ an unknown surface or shading color is refused with an error and the document is
|
||||
{"name":"run_command","arguments":{"command":"effect.apply","params":{"effect":"threeD.classicExtrude","item":null,"params":{"depth":40,"bevel":"rounded","bevelHeight":6,"surface":"diffuse","blendSteps":64}}}}
|
||||
```
|
||||
|
||||
## Revolve (Classic)
|
||||
|
||||
`effect.apply {effect: "threeD.classicRevolve", params: {...}, ids?, item?}` is Effect › 3D and Materials ›
|
||||
3D (Classic) › Revolve (Classic)…. It sweeps the profile round a vertical axis as `threeD.revolve` does, at 64 segments
|
||||
for a full turn, and draws every face, back to front, with the classic surfaces. Parameters: `angle` (degrees,
|
||||
0..360, default 360), `cap` (true closes the profile's open ends, false leaves the surface hollow), `offset` (points,
|
||||
0..1000, default 0; the axis's distance from the profile, so a value above 0 makes a ring), `edge` (`left`, the
|
||||
default, or `right`: the edge of the profile's bounds the axis is at), `rotationX/Y/Z` and `perspectiveAngle` as
|
||||
above, and `surface`, the lights, `ambient`, `highlightIntensity`, `highlightSize`, `blendSteps`, `shadingColor` and
|
||||
`shadingCustom` as Extrude & Bevel (Classic) takes them. It has no `drawHiddenFaces`. The effect also goes on a group
|
||||
or layer, whose members turn about one shared axis, or on a fill or stroke. The public documentation describes the
|
||||
dialog ([Create 3D objects](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/create-3d-objects.html))
|
||||
and its Angle (0 to 360), Cap, Offset (0 to 1000) and Left Edge or Right Edge settings
|
||||
([Revolve options](https://helpx.adobe.com/pdf/cs6/illustrator_reference.pdf#page=283));
|
||||
the defaults are VectorCraft's own. An unknown parameter (`segments` included), a number out of range, a flag that
|
||||
is not true or false, another `edge`, surface or shading color is refused with an error and the document is not
|
||||
changed.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"effect.apply","params":{"effect":"threeD.classicRevolve","item":null,"params":{"angle":270,"offset":10,"surface":"diffuse","blendSteps":64}}}}
|
||||
```
|
||||
|
||||
## Rotate (Classic)
|
||||
|
||||
`effect.apply {effect: "threeD.classicRotate", params: {...}, ids?, item?}` is Effect › 3D and Materials ›
|
||||
3D (Classic) › Rotate (Classic)…. It turns every fill and stroke as one flat plane about the center of the object's
|
||||
bounds, as `threeD.rotate` does, each in its own paint, and draws the plane from either side. Parameters:
|
||||
`rotationX/Y/Z` and `perspectiveAngle` as above; `surface` (`diffuse`, the default, lights the art softly, and
|
||||
`none` keeps the paint unlit); and the lights, `ambient`, `blendSteps`, `shadingColor` and `shadingCustom` as Extrude &
|
||||
Bevel (Classic) takes them. It has no highlight, no `drawHiddenFaces` and no shadow. The effect also goes on a group
|
||||
or layer, whose members turn about one shared center, or on a fill or stroke. The public documentation describes the
|
||||
dialog's Position and Surface settings
|
||||
([Rotate objects in 3D](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/rotate-objects-in-3d.html)),
|
||||
the Position presets, rotations from -180 to 180 and Perspective from 0 to 160
|
||||
([Set 3D rotation position options](https://help.adobe.com/archive/en_US/illustrator/cs5/illustrator_cs5_help.pdf#page=270)),
|
||||
and No Shading and Diffuse Shading as the only surfaces for an object that uses only this effect
|
||||
([Surface shading options](https://help.adobe.com/archive/en_US/illustrator/cs5/illustrator_cs5_help.pdf#page=271)).
|
||||
The defaults and the shading are VectorCraft's own. An unknown parameter (`highlightSize` and `drawHiddenFaces`
|
||||
included), a number out of range, a flag that is not true or false, a surface other than `none` or `diffuse`, or
|
||||
another shading color is refused with an error and the document is not changed.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"effect.apply","params":{"effect":"threeD.classicRotate","item":null,"params":{"rotationX":0,"rotationY":40,"rotationZ":0,"surface":"diffuse","blendSteps":64}}}}
|
||||
```
|
||||
|
||||
|
||||
## Edit Colors and Recolor Artwork
|
||||
|
||||
@@ -3356,6 +3457,16 @@ that shape, and the effects before it are left out. Its Contents row stands for
|
||||
vector exports write those items as paths, and `effect.expandAppearance` turns them into objects in a group with the
|
||||
image.
|
||||
Use Preview Bounds measures an image by its frame.
|
||||
A Warp effect (`warp.arc` … `warp.twist`) on an image, or on a group that holds one, bends the image's pixels with its
|
||||
frame: after the last effect in the list, the image is cut into 10 × 10 cells of two triangles, each a clipped piece of
|
||||
the image under its own affine map. The cell count is VectorCraft's own. Effects after the Warp reshape only the frame,
|
||||
a second Warp bends the same pieces, and a Transform effect moves them. A placed gradient on a group that holds the
|
||||
image follows the group's bounds as the pieces draw them. The pieces keep the
|
||||
image's fill color when it colorizes a grayscale image, and so do the pieces an envelope cuts. Under any geometry
|
||||
effect, an image keeps that color and is drawn with or without smoothing as the image is. SVG writes the image once and
|
||||
draws the pieces with `<use>`; PDF, EPS, `.ai` and PNG write the bent pieces. Removing the warp shows the image as it
|
||||
was. The images stay unbent when their pieces would make the art pass 2 million objects, and an image stays unbent when
|
||||
its clipping frame keeps its shape because the warp would pass the effects' anchor budget.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"appearance.addFill","params":{"ids":[7]}}}
|
||||
@@ -3482,8 +3593,8 @@ error.
|
||||
|
||||
## Brush libraries
|
||||
|
||||
Brush libraries are read-only sets of brushes: six generated in code (Art Ribbons, Calligraphic Nibs, Pattern
|
||||
Borders, Scatter Shapes, Game Trails, Energy Beams), the `.vectorcraft` files in the user library folder of the desktop app (User
|
||||
Brush libraries are read-only sets of brushes: seven generated in code (Art Ribbons, Calligraphic Nibs, Bristle
|
||||
Brushes with one bristle brush for each tip shape, Pattern Borders, Scatter Shapes, Game Trails, Energy Beams), the `.vectorcraft` files in the user library folder of the desktop app (User
|
||||
Defined) and the brushes of any document loaded with `brush.loadLibrary` (a document without brushes of its own has
|
||||
the default ones). `brush.libraries` lists them (`id`, `name`, `category`: `builtIn`, `user` or `loaded`, `count`) and
|
||||
`brush.library {library}` returns one's brushes (`name`, `type`; `library` is an id or a name).
|
||||
@@ -5074,7 +5185,8 @@ Pen's second anchor at (150, 431), in line with the square's centre.
|
||||
`type.resetTracking` (Cmd+Alt+Q, `press_key {key: "Q", mods: {cmd: true, alt: true}}`) sets tracking to 0 and
|
||||
clears manual kerning in the type objects in `ids`, the Type tool's selected text or the selected type objects,
|
||||
keeping Optical; at a caret
|
||||
it resets only the kerning of the character before it. `type.resetHorizontalScale` (Cmd+Shift+X,
|
||||
it resets only the kerning there: of the character before it, or at the start of a line (of the text, a paragraph,
|
||||
or after a forced line break) the space before the character after it (`kerningBefore`). `type.resetHorizontalScale` (Cmd+Shift+X,
|
||||
`press_key {key: "X", mods: {cmd: true, shift: true}}`) sets the horizontal scale to 100%, as the Character panel
|
||||
shows it, in the same text; at a caret it leaves the text as it is. In the app, `type.highlightKerning` (Cmd+Alt+K),
|
||||
`type.highlightTracking` (no default key) and `type.highlightFontName` (Cmd+Alt+Shift+F) show the Character panel and
|
||||
@@ -5679,13 +5791,23 @@ They also write the `copyrightNotice` as `dc:rights`, the `copyrightUrl` as `xmp
|
||||
`copyrightStatus` as `xmpRights:Marked` (`True` for copyrighted, `False` for public domain, none for unknown) to the
|
||||
XMP packet.
|
||||
A PDF, `.ai` or `.ait` opened from its pages or its editing copy (`document.open`,
|
||||
`file.newFromTemplate`, `file.revert`; not one restored from the document it carries) opens with the `author`,
|
||||
`description`, `keywords`, `copyrightStatus`, `copyrightNotice`, `copyrightUrl`, `created` and `modified` that its XMP
|
||||
packet (`dc:creator`, `dc:description`, `dc:subject` or `pdf:Keywords`, `xmpRights:Marked`, `dc:rights`,
|
||||
`xmpRights:WebStatement`, `xmp:CreateDate`, `xmp:ModifyDate`) and its document information dictionary (`Author`,
|
||||
`Subject`, `Keywords`, `CreationDate`, `ModDate`) give, the XMP packet's value where both give one. A value that
|
||||
`file.info` would refuse is left out, and the title is set as for any other open (the file name, or a new untitled
|
||||
name for a template).
|
||||
`file.newFromTemplate`, `file.revert`; not one restored from the document it carries) opens with the `title`,
|
||||
`author`, `description`, `keywords`, `copyrightStatus`, `copyrightNotice`, `copyrightUrl`, `created` and `modified`
|
||||
that its XMP packet (`dc:title`, `dc:creator`, `dc:description`, `dc:subject` or `pdf:Keywords`, `xmpRights:Marked`,
|
||||
`dc:rights`, `xmpRights:WebStatement`, `xmp:CreateDate`, `xmp:ModifyDate`) and its document information dictionary
|
||||
(`Title`, `Author`, `Subject`, `Keywords`, `CreationDate`, `ModDate`) give, the XMP packet's value where both give
|
||||
one (for `dc:title` and `dc:description`, the `x-default` language item first). An SVG opens with the `title` of its
|
||||
root `<title>` element (not a shape's or group's). A title is read with its white space made single spaces and
|
||||
without control characters, at most 65,536 characters, as `file.info` takes one. A value that `file.info` would
|
||||
refuse is left out. A file that gives no title, a blank one or the `Untitled-N` name New gives leaves File Info with
|
||||
the document's name as its title, and a template opens with a new untitled name.
|
||||
The title a file gave is File Info's alone: the document's name, which is what its tab shows until it has a file and
|
||||
what Save As and the default export names start from, stays the file's name with its extension (`box.svg`), or the
|
||||
title a native file holds. File Info's `title` is the one the file gave, else the document's name. It is saved with the
|
||||
document (a native file, and the editing data of an SVG, PDF, `.ai` or EPS that carries it), and the files an export
|
||||
writes carry it (PDF `Title` and XMP `dc:title`, SVG `<title>` and Dublin Core, PNG text, EPS and `.ai` `%%Title`),
|
||||
where the document has one. Changing the `title` with `file.info` sets the document's name, as it always did, and drops
|
||||
the title the file gave.
|
||||
`document.inspect` (`inspect_document`) and the `document.open` (`open_file`) reply report the File Info title as
|
||||
`fileInfoTitle`. Their `title` is the document's tab name, which comes from the file name once the document is saved
|
||||
to or opened from a file (with ` [Converted]` or ` [Recovered]` where that applies).
|
||||
@@ -6412,13 +6534,68 @@ from the origin, so P2 lies there when the file sets no scaling points.
|
||||
`SP0` draws nothing. Labels, the symbols of symbol mode (`SM`) and raster images are not drawn, the input window
|
||||
(`IW`) does not clip the art, the art of later pages is drawn over the first page, and hatched and patterned fills are
|
||||
filled solid; `warnings` lists each of these. A file named as a plot file that doesn't start with plotter
|
||||
instructions is refused with "not an HP-GL file". VectorCraft does not write HP-GL.
|
||||
instructions is refused with "not an HP-GL file".
|
||||
|
||||
```json
|
||||
{"name":"open_file","arguments":{"path":"/tmp/part.plt"}}
|
||||
{"name":"run_command","arguments":{"command":"file.place","params":{"path":"/tmp/part.plt","at":[300,200]}}}
|
||||
```
|
||||
|
||||
## HP-GL export
|
||||
|
||||
`document.export` (and `export`, and Export As) with format `hpgl` writes a plot file (`.plt`) of the first artboard
|
||||
(or `artboard`) for pen plotters and cutters; `useArtboards: true` writes one file per chosen artboard
|
||||
(`{stem}-{artboard}.plt`), `false` the bounds of the visible art. Each visible path is drawn once along its own line:
|
||||
a filled shape is its closed outline, also when a stroke gives its color, and a stroked path is its center line, so
|
||||
fills, stroke widths, dashes, brushes and arrowheads are not painted. Type is drawn as its glyph outlines, and a
|
||||
gradient mesh as its outline, also inside a blend (a mesh whose every point is at 0% opacity draws nothing). A
|
||||
symbol instance is drawn as its symbol's art with the art's effects, in the colors of the instance's stain.
|
||||
Clipping masks cut the art they clip, and the art is cut at the artboard's edges. Hidden and non-printing layers,
|
||||
hidden objects, objects at 0% opacity, template layers and guides are left out, and so are placed images, with a
|
||||
warning. Coordinates are plotter units (1016 to the inch, y up) from the artboard's bottom-left corner, or its
|
||||
center with `origin: "center"`, rounded to whole units, as the HP-GL/2 and HP RTL Reference Guide defines them
|
||||
([book listing](https://informit.com/store/hp-gl-2-and-hp-rtl-reference-guide-a-handbook-for-program-9780201310146);
|
||||
the [vendor's support page](https://support.hp.com/sg-en/document/c01121263) names it as the language reference).
|
||||
The guide also gives the instruction syntax and the encoding of `PE` numbers that the writer follows. Art more than
|
||||
8,388,607 units (about 210 m) from the origin is cut off, with a warning.
|
||||
|
||||
Each color is a pen. Pen 1 is the first color the plot draws in, pen 2 the next new one, and so on, up to 255 pens;
|
||||
colors past those draw with the pen of the nearest color. The files of one export share the numbering, so a color is
|
||||
the same pen in each file. `pens: ["#000000", "#ff0000"]` gives the pen table instead (pen 1 first): each path draws
|
||||
with the pen of the nearest color, the lower pen when two are as near. A path's color is its topmost visible stroke's,
|
||||
else its topmost visible fill's, where a fill or stroke at 0% opacity does not count. A gradient's color is the average
|
||||
of its stops, a gradient mesh's the average of its points' colors (points at 0% opacity left out), and a pattern's its first visible solid fill (gray
|
||||
when it has none). The pens draw in turn, each pen's paths in paint order, or with `sortPaths: true` each next path
|
||||
the one that starts nearest where the pen stopped (an open path may be drawn from its other end, and a closed one
|
||||
starts at its point nearest the pen). With `insideFirst: true`, a path is drawn only after every path of the same
|
||||
pen that lies inside it, for example a letter's counter before the letter and a part's holes before the part. A
|
||||
path lies inside a closed path when it does not cross that path's line, every part of it lies inside that path (by
|
||||
the even-odd rule) or on its line, and some part lies inside: a path touching the line around it is inside, a path
|
||||
crossing it or leaving it through a corner is not, and copies of one outline are not inside each other. Without
|
||||
`sortPaths` the paths otherwise keep paint order; with it, the next path is the nearest of those free to draw.
|
||||
Curves become straight moves no further than `curveTolerance` points from the curve (default 0.1, from 0.01 to 10).
|
||||
These defaults, the 255-pen limit and the rules for pen numbers, colors and the order of paths above are
|
||||
VectorCraft's own.
|
||||
|
||||
`hpglVersion: "hpgl"` (the default) writes `IN;`, then for each pen `PU;SP<n>;` and each path `PUx,y;PDx,y,…;`, and
|
||||
ends with `PU;SP0;`. `"hpgl2"` also writes the palette (`NP` and a `PC` color for each pen) and draws each pen's
|
||||
paths as one encoded polyline (`PE`, base 64), which takes fewer bytes than the same moves as text. An `"hpgl"` plot
|
||||
with a coordinate outside −32,768 to 32,767 units (about 82 cm from the origin, the range of 16-bit numbers) is written
|
||||
as it is, with a warning that older devices may not read it; this check is VectorCraft's own. `warnings` names
|
||||
type drawn as outlines, inline graphics in type (left out), placed images, opacity masks, raster effects, stroke
|
||||
looks that are not drawn, art past the plotter's range, HP-GL coordinates past 16-bit numbers, colors past 255 pens,
|
||||
and the limits on points, clipping work, symbols, live art, nesting depth and sorting. Sorting and finding which paths lie inside which share one
|
||||
work budget for the plot, 500 million box and point comparisons. When sorting runs out of it, the paths not yet
|
||||
drawn keep paint order, each after the paths inside it with `insideFirst`. When it runs out while the writer is
|
||||
finding which paths lie inside which, the paths of that pen and of the later pens keep paint order. Both cases add
|
||||
a warning.
|
||||
|
||||
```json
|
||||
{"name":"export","arguments":{"path":"/tmp/decal.plt","format":"hpgl"}}
|
||||
{"name":"run_command","arguments":{"command":"document.export","params":{"format":"hpgl","path":"/tmp/sheet.plt","useArtboards":true,"hpglVersion":"hpgl2","pens":["#000000","#ff0000"],"sortPaths":true}}}
|
||||
{"name":"run_command","arguments":{"command":"document.export","params":{"format":"hpgl","path":"/tmp/stencil.plt","sortPaths":true,"insideFirst":true}}}
|
||||
```
|
||||
|
||||
## TIFF, BMP and Targa export
|
||||
|
||||
`document.export` (and `export`) writes them like the other raster formats: one artboard (or `useArtboards`, one
|
||||
@@ -7217,6 +7394,25 @@ Panel sets it back to Push Out Only. One undo step.
|
||||
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"kinsokuType":"pushInFirst"}}}
|
||||
```
|
||||
|
||||
## Korean Line Break Anywhere
|
||||
|
||||
`text.setFormat {koreanLineBreakAnywhere: bool}` (Paragraph panel menu › Korean Line Break Anywhere, shown with the
|
||||
East Asian options) turns it on or off for the paragraphs of the selected type (or `ids`, or the paragraphs a `start` /
|
||||
`end` range touches). Off, a Korean word (the characters between two spaces, when any of them is Hangul, Han and kana
|
||||
included) breaks only at its spaces, after East Asian punctuation in it, and at forced line breaks, so a word that does
|
||||
not fit moves to the next line whole; a word wider than the line still breaks where it overflows. On, a line may break
|
||||
between any two Hangul syllables. Both composers and vertical type follow it, No Break and the Kinsoku Set still apply,
|
||||
and hyphenation never adds a hyphen to Hangul. Text without Hangul keeps its own breaks. New type and imported text have
|
||||
it off; paragraphs saved before the option open with it on, the breaks they were laid out with. It is saved with the
|
||||
paragraph (`korean_line_break_anywhere` in `para`) only when off, paragraph styles carry it (Paragraph Style Options ›
|
||||
East Asian Composition Settings), and Reset Panel turns it off. A value that is not true or false is refused. One undo
|
||||
step. `.ai` editing data does not hold it, and a save reports a Korean paragraph with it on as left out. The
|
||||
help page: https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/set-line-breaks-korean-text.html
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"koreanLineBreakAnywhere":true}}}
|
||||
```
|
||||
|
||||
## Hanging punctuation (burasagari)
|
||||
|
||||
`text.setFormat {burasagari: "none"|"standard"|"forced"}` sets the Paragraph panel menu's Burasagari (None / Regular /
|
||||
@@ -7776,6 +7972,26 @@ begins or ends, dragging the centre bracket slides the type along its path and,
|
||||
{"name":"run_command","arguments":{"command":"type.pathOptions","params":{"start":0.25,"end":0.75,"flip":true}}}
|
||||
```
|
||||
|
||||
`type.pathCenter.top {ids?|id?}` and `type.pathCenter.bottom {ids?|id?}` (Type › Type on a Path › Center on Top and
|
||||
Center on Bottom, and the Contextual Task Bar for type on a closed path) slide the selected type on a closed path along
|
||||
it so that the middle of the span between its brackets lands on the path's topmost or bottommost point. This is
|
||||
VectorCraft's own behavior. The path is measured as the object is drawn, after its transform, so a rotated, scaled or
|
||||
skewed circle centers on its drawn top. On a level stretch, such as a rectangle's top edge, the middle of the stretch
|
||||
is used; with several separate topmost points, the middle of the shortest part of the path that holds them all is
|
||||
used. The span keeps its length, and type that runs once round stays once round. The first paragraph's alignment
|
||||
becomes Center unless it is Center or Justify Center, so the type is centered there. The type is not flipped: on the
|
||||
bottom of a circle, whose path runs clockwise, it reads upside down outside the circle until `type.pathOptions {flip:
|
||||
true}` turns it inside. Point type and area type in the selection are left as they are. Type on an open or compound
|
||||
path, type in a thread, and type on a path with no length or no height are refused with an error that names them,
|
||||
and nothing changes. The menu items are enabled, and the task bar shows them, only while every selected type on a
|
||||
path is on one closed path and none of it is in a thread. Several objects move in one undo step. When every object is
|
||||
already centered there, nothing changes and no undo step is recorded. Each replies `{ids, start, end}` with the first
|
||||
object's brackets.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"type.pathCenter.top","params":{}}}
|
||||
```
|
||||
|
||||
## Dynamic Text
|
||||
|
||||
`type.dynamicText {on?: bool, ids?}` (Type › Dynamic Text, and the Contextual Task Bar for type on a path) turns
|
||||
@@ -7793,12 +8009,12 @@ its look, in one undo step. Point type in the selection is left as it is, and ar
|
||||
([Dynamic Text: Block](#dynamic-text-block)). Type on a path in a
|
||||
thread is refused with an error that names it, and the menu item and the task bar item are not offered for it;
|
||||
`text.thread.create` and `text.thread.add` turn Dynamic Text off on the frames they thread, at their drawn sizes, and
|
||||
list them in `dynamicTextOff`. When every object already has the setting asked for, nothing changes and no undo step
|
||||
list them in `dynamicTextOff`; `addon.type.releasePath` turns it off at the drawn sizes on the point type it makes. When every object already has the setting asked for, nothing changes and no undo step
|
||||
is recorded. It replies `{on, ids, fitScale}` (the first object's factor, 1 when off), and the menu item is checked
|
||||
while the first selected type on a path or area type has it on. `document.inspect` reports `dynamicText` and `fitScale` for type
|
||||
on a path. New type and documents from before it have it off, and it is saved only when on. SVG, PDF, EPS and the
|
||||
editing data for other apps write the type at its drawn sizes; the editing data does not hold the setting and says
|
||||
so in a warning.
|
||||
on a path. New type and documents from before it have it off, and it is saved only when on. SVG, PDF, EPS, legacy
|
||||
`.ai` and the editing data for other apps write the type at its drawn sizes; legacy `.ai` and the editing data do not
|
||||
hold the setting, and each says so in a warning.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"type.dynamicText","params":{"on":true}}}
|
||||
@@ -7841,8 +8057,9 @@ targeted objects, as before. With a range, the character attributes of `text.set
|
||||
that range of characters. When every paragraph that `text.tabs.set` or `text.tabs.clear` targets already has those
|
||||
stops, the command changes nothing and records no undo step. `text.tabs.set` rejects a supplied `align`, `leader`, or
|
||||
`alignOn` value that is not a string, an empty `alignOn` value or one of more than one character, a leader of more than
|
||||
8 characters, a position outside 0 to 100000 pt, and more than 256 stops. It sorts the stops by position and keeps the
|
||||
first of stops at one position.
|
||||
8 characters, a position outside 0 to 100000 pt, and more than 256 stops unless every paragraph it targets already
|
||||
holds at least that many, so stops can be removed from a paragraph that holds more. It sorts the stops by position and
|
||||
keeps the first of stops at one position.
|
||||
|
||||
`text.tabs.get {start?}` and `paraStyle.new` / `paraStyle.redefine {id, start?}` read the
|
||||
paragraph at `start`; `paraStyle.list` counts paragraphs as uses, and `charStyle.list` counts runs; both count the type in
|
||||
@@ -7924,8 +8141,8 @@ the Paragraph Styles panel, or Paragraph Style Options… in its menu, does the
|
||||
`paraStyle.edit {name, to, attrs}`, which takes the same arguments as `charStyle.edit` and replaces the style's
|
||||
name and attributes in one undo step; the paragraphs that use the style keep their local overrides. OK with no field
|
||||
changed records no undo step, and Preview updates the paragraphs while the dialog is open (Cancel restores them).
|
||||
The `paraStyleOptions` dialog exposes `name`, `section` (`general`, `basic`, `tabs`, `composition`, `hyphenation` or
|
||||
`justification`) and `preview`, and these fields:
|
||||
The `paraStyleOptions` dialog exposes `name`, `section` (`general`, `basic`, `tabs`, `composition`, `hyphenation`,
|
||||
`justification` or `eastAsian`) and `preview`, and these fields:
|
||||
|
||||
- Basic Paragraph Formats: `justify` (`Auto`, `Left`, `Center`, `Right`, `JustifyLeft`, `JustifyCenter`,
|
||||
`JustifyRight` or `JustifyAll`), and `left_indent`, `right_indent`, `first_line_indent`, `space_before` and
|
||||
@@ -7944,6 +8161,8 @@ The `paraStyleOptions` dialog exposes `name`, `section` (`general`, `basic`, `ta
|
||||
- Justification: `word_min`, `word_desired`, `word_max`, `letter_min`, `letter_desired`, `letter_max`, `glyph_min`,
|
||||
`glyph_desired` and `glyph_max` (percent), `auto_leading` (percent) and `single_word` (`fullJustify`,
|
||||
`alignLeft`, `alignCenter` or `alignRight`).
|
||||
- East Asian Composition Settings: the `korean_line_break_anywhere` checkbox (Korean Composition Settings › Korean
|
||||
Line Break Anywhere).
|
||||
|
||||
A field left as it was keeps the attribute as the style has it, and an empty field leaves its attribute unset; an
|
||||
empty Hyphenation or Justification field takes its default while another field of that section is set. Attributes
|
||||
@@ -7958,8 +8177,9 @@ position. A value the style already has is kept. An invalid value leaves the
|
||||
style, its paragraphs and Undo history unchanged, and keeps the dialog open.
|
||||
Reset Panel removes the section's attributes from the definition: Basic Paragraph Formats removes `justify` and the
|
||||
five distances, Tabs removes `tabs`, Composition removes `composer` and `roman_hanging`, Hyphenation removes `hyphenate` and
|
||||
`hyphenation`, and
|
||||
Justification removes `justification`. `ui.paraStyleOptions.resetPanel {}` performs that reset for the section
|
||||
`hyphenation`,
|
||||
Justification removes `justification`, and East Asian Composition Settings removes `korean_line_break_anywhere`.
|
||||
`ui.paraStyleOptions.resetPanel {}` performs that reset for the section
|
||||
selected in Paragraph Style Options or New Paragraph Style; it is disabled on General.
|
||||
|
||||
In the native file a text object's `para` holds the first paragraph's attributes and `paras` (left out when every
|
||||
@@ -8448,6 +8668,20 @@ share of the content's width or height in proportion to the length of its contro
|
||||
its curve parameter, so content spreads evenly along a segment without handles. An envelope opened from an `.ai` file
|
||||
keeps the corners the file saves, and they stay on the same anchors when the envelope is moved, rotated or
|
||||
reflected. These are VectorCraft's own rules, and existing top-object envelopes redraw by them.
|
||||
A warp bends the content's bounds as one Bézier patch. The style sets the patch's control points from the bend and
|
||||
the size of the bounds (Arc's top and bottom edges, for example, are cubic curves close to arcs around one center), then
|
||||
the horizontal distortion scales each column of control points about its middle, and the vertical distortion scales
|
||||
each row. Content with no width or no height takes the middle of the patch along that side. New warp envelopes, and
|
||||
those opened from `.ai` files, carry `version` 1 for this map, and `object.envelope.info` reports it. Envelopes in
|
||||
`.vectorcraft` documents saved before the version existed read as `version` 0, the earlier map, and keep their look,
|
||||
and editing an envelope keeps its version. `version` (0 or 1) on `object.envelope.makeWithWarp`,
|
||||
`object.envelope.resetWithWarp` and `object.envelope.options` sets the map. The Warp effects (`warp.arc` …
|
||||
`warp.twist`) take the same map through the same `version` param: `effect.apply` gives a new Warp `version` 1 unless
|
||||
`params.version` is 0, the built-in graphic styles' Warps have 1, and Effect › Warp edits keep each Warp's own version.
|
||||
These commands refuse any other value with `` `version` must be 0 or 1 ``; `1.0` counts as 1. An `.ai` file holds a
|
||||
warp's settings and not its version, so a `version` 0 warp saved to `.ai` and opened again draws with `version` 1.
|
||||
These patches are VectorCraft's own rules; the styles and the ranges of Bend and the distortions are those of the Warp
|
||||
options (https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/distort-and-transform-effects-in-illustrator.html).
|
||||
`object.envelope.info` reads the selected envelope's settings (`type`, the warp's or mesh's values, `fidelity` and
|
||||
the options, `editing`), or with no envelope selected the options new envelopes get. With an envelope selected,
|
||||
`object.envelope.resetWithWarp` and `object.envelope.resetWithMesh` (`rows`, `cols`, `maintainShape`: true keeps
|
||||
@@ -9284,6 +9518,60 @@ whole points and Escape cancels. A drag keeps a Contour's own corners and miter
|
||||
options `corners` and `miterLimit` (`tool.setOption`). A border with a gap is two Contours of one shape (the gap first, so
|
||||
it ends up in front), baked with `effect.expandAppearance`, then `object.pathfinder.minusFront`.
|
||||
|
||||
## Cutting Tools add-on
|
||||
|
||||
Cutting Tools is a built-in add-on that prepares art for cutting. Its command is `addon.cutting.bridges`; in the desktop
|
||||
app it is Object › Plug-ins › Cutting Tools › Stencil Bridges….
|
||||
|
||||
### Stencil Bridges
|
||||
|
||||
`addon.cutting.bridges` cuts bridges out of the selected art so that a stencil cut from it holds together. An island is
|
||||
an area the art encloses, such as the inside of an O, A or 8 or a hole in a shape; cut out of a sheet, it would fall
|
||||
away. A bridge is a strip of the sheet left uncut across the art, which keeps the island attached
|
||||
([stencil islands and bridges](https://en.wikipedia.org/wiki/Stencil)). The rules below are VectorCraft's own.
|
||||
|
||||
- The art is measured by the area its outlines enclose, whatever their paint: paths and compound paths with their fill
|
||||
rule (open paths closed as a fill closes them), groups by their members, and type by its glyph outlines. Hidden
|
||||
objects, and the members of hidden groups, are left out. The type object itself is not converted. An outline that
|
||||
encloses less than the square of the curve tolerance (below) counts as part of the area around it.
|
||||
- Every island is joined to the area around the art, directly or through another island, by the shortest crossings
|
||||
that fit (among crossings of about the same length, the one nearest the middle of the island). Then each island gets
|
||||
more bridges, up to `count`: each at least two widths from the island's other bridges and no longer than twice the
|
||||
shortest crossing a bridge fits plus `width`. An island too narrow for a strip `width` wide gets no bridge, and islands that a
|
||||
bridge could join only to each other and not to the sheet get none either. `unbridged` counts the islands the cut
|
||||
result still encloses.
|
||||
- The result is one compound path (a plain path when one outline is left) with the strips cut out of its outlines,
|
||||
painted as the topmost selected shape, in the place of the topmost selected object that has outlines. It is selected.
|
||||
Selected objects with no outlines in the art, such as hidden objects or type holding only spaces, stay where they are.
|
||||
- With `keepOriginal` the selected objects that make up the art move to a new hidden layer named "Stencil Original"
|
||||
("Stencil Original 2" and so on when that name is taken), above the other layers, in their stacking order and outside
|
||||
the groups that held them; with false they are replaced.
|
||||
- Art with no islands, or with islands that no bridge of the width fits, is left as it is: the result carries a
|
||||
`warning` and no undo step is made. Otherwise the run is one undo step, with a `warning` when the cut result still
|
||||
encloses some islands.
|
||||
- Curves are followed in straight segments within a twentieth of `width` (0.002 to 0.25 pt); a curve that would need
|
||||
more than 4096 segments gets a larger tolerance. Art that reaches more than 10000000 pt from the origin or has more
|
||||
than 200000 anchors is refused before it is combined. Art is also refused when its combined area has more than 4000
|
||||
outlines, when its outlines need more than 200000 straight segments, and when finding which outlines lie inside
|
||||
which, or measuring the crossings until every island is joined, takes more work than a fixed budget allows. When the
|
||||
budget runs out while extra bridges are being added, the bridges found so far are kept.
|
||||
|
||||
| Parameter | Values (default) |
|
||||
|---|---|
|
||||
| `width` | each bridge's width, 0.01 to 1000 pt, as a number of points or a string with a unit such as `"2 mm"` (2) |
|
||||
| `count` | the bridges each island gets, a whole number from 1 to 16; fewer where it has no room, and more where other islands are joined to the sheet through it (1) |
|
||||
| `direction` | `vertical`, `horizontal`, or `shortest` (the shortest crossing among 12 directions 15° apart) (`vertical`) |
|
||||
| `keepOriginal` | true; false replaces the selected art |
|
||||
|
||||
→ `{id (the new path, or null), islands, bridges, unbridged, original (the hidden layer's id, or null), warning?}`.
|
||||
Stencil Bridges… opens the command dialog (`ui.paramDialog`) on Width, Count, Direction (Vertical, Horizontal, Shortest
|
||||
Crossing) and Keep Original.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"addon.cutting.bridges","params":{"width":"2 mm"}}}
|
||||
{"name":"run_command","arguments":{"command":"addon.cutting.bridges","params":{"width":3,"count":2,"direction":"shortest","keepOriginal":false}}}
|
||||
```
|
||||
|
||||
## Color Effects add-on
|
||||
|
||||
Color Effects is a built-in add-on: its effects are colour adjustments in Effect › Color Effects, its command is
|
||||
@@ -9654,7 +9942,9 @@ VectorCraft addition and changes no parity score.
|
||||
- **The text** keeps its runs with their character attributes and its paragraph attributes, and becomes point type on
|
||||
one straight line along the object's own axis, with the baseline starting where the first character sat on the path
|
||||
(type with no characters starts at its start bracket). Type on a Path Options (effect, align to path, spacing, flip,
|
||||
brackets) don't apply to point type and are dropped. Without `keepOriginal` the type object itself becomes the point
|
||||
brackets) don't apply to point type and are dropped. Type with Dynamic Text on (`type.dynamicText`) becomes point
|
||||
type with it off, at the sizes it was drawn at: each size, leading and baseline shift is multiplied by the fit factor.
|
||||
Without `keepOriginal` the type object itself becomes the point
|
||||
type, keeping its id, so a frame of threaded text leaves its thread with the text it holds, as
|
||||
`type.convertToPointType` does for area type.
|
||||
- It acts on the type on a path among `ids` (or `id`, else the selection), groups included; other objects are left
|
||||
|
||||
+151
-157
@@ -1,9 +1,9 @@
|
||||
# Plug-ins: the WebAssembly plug-in API (ABI v1)
|
||||
|
||||
VectorCraft does not host native plug-ins built for other illustration apps: that needs unsafe FFI into
|
||||
arbitrary machine code and cannot work in the browser build. Instead, plug-ins are **WebAssembly modules**
|
||||
run in a sandbox. One `.wasm` file works on macOS, Windows, Linux and the web. The design follows PhotoCraft's
|
||||
plug-in API (same sandbox, same manifest and parameter schema), extended for vector documents.
|
||||
arbitrary machine code and cannot work in the browser build. Instead, plug-ins are **WebAssembly modules** run in a
|
||||
sandbox. One `.wasm` file works on macOS, Windows, Linux and the web. The design follows PhotoCraft's plug-in API (same
|
||||
sandbox, same manifest and parameter schema), extended for vector documents.
|
||||
|
||||
There are four kinds of plug-in:
|
||||
|
||||
@@ -20,8 +20,8 @@ There are four kinds of plug-in:
|
||||
paths and compound paths. File › Open lists their files, File › Open With lists the importers by name, and the host
|
||||
hands them the file's bytes.
|
||||
|
||||
This page is the contract for plug-in authors and the reference for the host (`crates/plugins`, the
|
||||
`plugin.*` commands in `crates/engine/src/cmd/plugin.rs`).
|
||||
This page is the contract for plug-in authors and the reference for the host (`crates/plugins`, the `plugin.*` commands
|
||||
in `crates/engine/src/cmd/plugin.rs`).
|
||||
|
||||
## Using plug-ins
|
||||
|
||||
@@ -44,14 +44,15 @@ An exporter adds a write-only format, `plugin.<id>`, which `document.formats` li
|
||||
its extensions that no built-in format has; `selectedOnly` hands it the selection, as for the built-in formats.
|
||||
|
||||
An importer adds a read-only format, `plugin.<id>`, which `document.formats` lists with its `plugin` id. `openExtensions`
|
||||
holds the extensions File › Open opens with the importer (the lists below describe which are left out). `document.open {"path"}` (or `{"name", "dataBase64"}`) opens a file with it when no built-in format
|
||||
reads the file, by its content or by its extension, and the importer lists the file's extension; when several do, the
|
||||
one whose id sorts first opens it. `document.open {"importer": "plugin.<id>", "params"?: {…}}` names the importer,
|
||||
which must list the file's extension, also one a built-in format reads (an importer that lists `svg` opens an `.svg`
|
||||
only this way). A damaged file of a built-in format reports that format's error, and the file is not handed to an
|
||||
importer. The importer's objects become a new document whose artboard is their bounds (at least 1 pt wide and high,
|
||||
and within ±4,000,000 pt); the document keeps no path, so Save asks for a name. File › Place reads only the built-in formats, and a file only an importer reads opens as a new document when it is dropped on the canvas. These rules are
|
||||
VectorCraft's own.
|
||||
holds the extensions File › Open opens with the importer (the lists below describe which are left out). `document.open
|
||||
{"path"}` (or `{"name", "dataBase64"}`) opens a file with it when no built-in format reads the file, by its content or
|
||||
by its extension, and the importer lists the file's extension; when several do, the one whose id sorts first opens it.
|
||||
`document.open {"importer": "plugin.<id>", "params"?: {…}}` names the importer, which must list the file's extension,
|
||||
also one a built-in format reads (an importer that lists `svg` opens an `.svg` only this way). A damaged file of a
|
||||
built-in format reports that format's error, and the file is not handed to an importer. The importer's objects become a
|
||||
new document whose artboard is their bounds (at least 1 pt wide and high, and within ±4,000,000 pt); the document keeps
|
||||
no path, so Save asks for a name. File › Place reads only the built-in formats, and a file only an importer reads opens
|
||||
as a new document when it is dropped on the canvas. These rules are VectorCraft's own.
|
||||
|
||||
In the app, installed filters appear under **Object › Plug-ins**, next to **Install Plug-in…** and **Reload
|
||||
Plug-ins**; live effects under **Effect › Plug-ins** and in the Appearance panel's effect menu. A plug-in with
|
||||
@@ -60,25 +61,23 @@ of numbers and colors, labelled, explained and sorted into sections as its manif
|
||||
parameters it runs at once. Edit › Keyboard Shortcuts lists each installed filter under Object › Plug-ins, where
|
||||
it can be given a shortcut (`plugin:<id>`); the shortcut stays saved while the plug-in is removed and applies again
|
||||
when it is reinstalled. Parameters too many for the window's height scroll inside the dialog, which keeps its
|
||||
heading and buttons in view. Exporters are formats of **File › Export › Export As**, where Use
|
||||
Artboards is greyed out (the plug-in writes the whole document); one with parameters asks for them in a dialog of the
|
||||
same kind once the file is picked, and its Export button writes the file. Importers' files are listed under File ›
|
||||
Open's "All readable files" and under a filter of their own, named after the importer (other open dialogs, such as New
|
||||
from Template and Load Styles, list only the files the built-in formats read). These lists leave out an importer's
|
||||
extensions that a built-in format reads, and those File › Open installs, imports or loads as something else (`.wasm`,
|
||||
`.kys`, `.jsx`, presets, libraries and swatch or graphic style libraries such as `.ase`). File › Open does not give
|
||||
those files to the importer, and Open With does. **File › Open With** lists the
|
||||
installed importers by name (`file.openWith {"importer", "path"?, "params"?}`), and each opens a file picked among its
|
||||
extensions with that importer (on the web, File › Open's picker opens instead) and adds it to Open Recent. On the
|
||||
desktop the importer runs off the UI thread, and a run that takes a while shows a progress window with Cancel, which
|
||||
stops it. An importer
|
||||
with parameters, given no `params`, asks for them in a dialog of the same kind once the file is picked, and its Open
|
||||
button opens the file; File › Open runs an importer with its parameters' defaults. Open Recent opens a file the way
|
||||
File › Open does: it does not remember the importer or the parameters given to Open With. A built-in format reads the
|
||||
file if one can, and otherwise the first importer by id that lists its extension reads it with its parameters' defaults.
|
||||
**File › Open** of a `.wasm` file installs
|
||||
a plug-in too (on the web this is how plug-ins get in). A heavy filter asks before it is installed, and runs in the
|
||||
background ([below](#heavy-filters)). A plug-in that lists permissions asks for them before it is installed
|
||||
heading and buttons in view. Exporters are formats of **File › Export › Export As**, where Use Artboards is greyed out
|
||||
(the plug-in writes the whole document); one with parameters asks for them in a dialog of the same kind once the file is
|
||||
picked, and its Export button writes the file. Importers' files are listed under File › Open's "All readable files" and
|
||||
under a filter of their own, named after the importer (other open dialogs, such as New from Template and Load Styles,
|
||||
list only the files the built-in formats read). These lists leave out an importer's extensions that a built-in format
|
||||
reads, and those File › Open installs, imports or loads as something else (`.wasm`, `.kys`, `.jsx`, presets, libraries
|
||||
and swatch or graphic style libraries such as `.ase`). File › Open does not give those files to the importer, and Open
|
||||
With does. **File › Open With** lists the installed importers by name (`file.openWith {"importer", "path"?,
|
||||
"params"?}`), and each opens a file picked among its extensions with that importer (on the web, File › Open's picker
|
||||
opens instead) and adds it to Open Recent. On the desktop the importer runs off the UI thread, and a run that takes a
|
||||
while shows a progress window with Cancel, which stops it. An importer with parameters, given no `params`, asks for them
|
||||
in a dialog of the same kind once the file is picked, and its Open button opens the file; File › Open runs an importer
|
||||
with its parameters' defaults. Open Recent opens a file the way File › Open does: it does not remember the importer or
|
||||
the parameters given to Open With. A built-in format reads the file if one can, and otherwise the first importer by id
|
||||
that lists its extension reads it with its parameters' defaults. **File › Open** of a `.wasm` file installs a plug-in
|
||||
too (on the web this is how plug-ins get in). A heavy filter asks before it is installed, and runs in the background
|
||||
([below](#heavy-filters)). A plug-in that lists permissions asks for them before it is installed
|
||||
([below](#permissions)).
|
||||
|
||||
### Commands
|
||||
@@ -117,9 +116,9 @@ the object is drawn as if the effect weren't there, and the record is kept for w
|
||||
|
||||
## The sandbox
|
||||
|
||||
The host is [`wasmi`](https://github.com/wasmi-labs/wasmi), a pure-Rust WebAssembly interpreter
|
||||
(MIT OR Apache-2.0). It builds for `wasm32-unknown-unknown` too, so plug-ins also run in the web build. Limits
|
||||
(`vectorcraft_plugins::Limits`, defaults):
|
||||
The host is [`wasmi`](https://github.com/wasmi-labs/wasmi), a pure-Rust WebAssembly interpreter (MIT OR Apache-2.0). It
|
||||
builds for `wasm32-unknown-unknown` too, so plug-ins also run in the web build. Limits (`vectorcraft_plugins::Limits`,
|
||||
defaults):
|
||||
|
||||
| Limit | Default | On violation |
|
||||
|---|---|---|
|
||||
@@ -155,15 +154,15 @@ measured with the page's `performance.now()`.
|
||||
Some filters do heavy but legitimate work, such as routing a plotter's pen, packing shapes onto a sheet or running a
|
||||
simulation, which a minute and 50 M instructions don't cover. Such a filter declares `"budget": "heavy"` in its
|
||||
manifest (only a filter may: a live effect runs whenever the document is drawn, an exporter has an export's budget and
|
||||
an importer an import's). Each of its runs gets 1 G instructions (20 times a light run's) plus the 4,000 per input byte, and 300 s
|
||||
(`vectorcraft_plugins::{HEAVY_FUEL, HEAVY_WALL_TIME_MS}`). Its output can't add to the budget: the host learns how
|
||||
much there is only once the run has ended.
|
||||
an importer an import's). Each of its runs gets 1 G instructions (20 times a light run's) plus the 4,000 per input byte,
|
||||
and 300 s (`vectorcraft_plugins::{HEAVY_FUEL, HEAVY_WALL_TIME_MS}`). Its output can't add to the budget: the host learns
|
||||
how much there is only once the run has ended.
|
||||
|
||||
- **Installing asks first.** Install Plug-in… and File › Open show what the filter asks for, and install it once the
|
||||
user clicks OK (see [Permissions](#permissions) for who can). `plugin.install` refuses it without `allowHeavy: true`,
|
||||
and refuses `allowHeavy` from a request except in a session started with `vectorcraft-cli --allow-plugin-grants`
|
||||
(`plugin.inspect` shows the budget beforehand). A filter kept in the `Plug-ins` folder was agreed to and is installed again
|
||||
at startup. The Additional Plug-ins Folder skips heavy filters with a note in its report, since loading a folder
|
||||
(`plugin.inspect` shows the budget beforehand). A filter kept in the `Plug-ins` folder was agreed to and is installed
|
||||
again at startup. The Additional Plug-ins Folder skips heavy filters with a note in its report, since loading a folder
|
||||
asks nobody.
|
||||
- **It runs in the background.** Its dialog has no Preview. OK (or the menu item, without parameters) runs it on a
|
||||
thread of its own while editing goes on, shown in the status bar and, after a moment, in a progress window with
|
||||
@@ -203,17 +202,15 @@ capability model proposed for PhotoCraft plug-ins
|
||||
`state`, which only its commands use. A filter may list any of its three, and `commands` with a `commandAllowlist`.
|
||||
Any kind may add `progress`, `settings` and `cache`, which no kind implies. An unknown name, a name listed twice, a
|
||||
name that doesn't apply to the kind, more than 16 names, `progress`, `settings`, `cache` or `commands` with an `api`
|
||||
below 2, or
|
||||
one of the names kept for a later plug-in API
|
||||
(`ui.panel`, `ui.tool`) is refused when the module is installed, and the error names it.
|
||||
below 2, or one of the names kept for a later plug-in API (`ui.panel`, `ui.tool`) is refused when the module is
|
||||
installed, and the error names it.
|
||||
- **Installing asks first.** Install Plug-in… and File › Open show the plug-in's name, id, version, the SHA-256 of
|
||||
the file and each permission in plain words, and install it once the user clicks OK, if the file still has the
|
||||
SHA-256 shown; Cancel installs nothing. The
|
||||
names shown have control and bidirectional characters escaped (`\u{202E}`) and are cut to 64 characters.
|
||||
Only a person can press its OK: `ui.dialog.confirm`, MCP's `answer_dialog` and injected input (`ui.key`, `ui.click`)
|
||||
are refused and the dialog stays open. Enter confirms it only when OK has the keyboard focus. `ui.dialog.set` can't
|
||||
change what it installs. OK stays disabled until the question has been on screen in the focused window for half a
|
||||
second (VectorCraft's own value, from the delay the security guidance at
|
||||
SHA-256 shown; Cancel installs nothing. The names shown have control and bidirectional characters escaped (`\u{202E}`)
|
||||
and are cut to 64 characters. Only a person can press its OK: `ui.dialog.confirm`, MCP's `answer_dialog` and injected
|
||||
input (`ui.key`, `ui.click`) are refused and the dialog stays open. Enter confirms it only when OK has the keyboard
|
||||
focus. `ui.dialog.set` can't change what it installs. OK stays disabled until the question has been on screen in the
|
||||
focused window for half a second (VectorCraft's own value, from the delay the security guidance at
|
||||
https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/security/security-considerations-for-browser-ui.md
|
||||
gives for dialog boxes), so a click, Enter or Space, or a press begun before then, installs nothing. The half second
|
||||
starts over for every new question, even one that reads the same, and when the window gets the focus back. Cancel
|
||||
@@ -280,23 +277,22 @@ capability model proposed for PhotoCraft plug-ins
|
||||
does not count for any session in the same process that uses the same `Plug-ins` folder, by any path to it, until
|
||||
`grants.json` can be written for that plug-in, as when the plug-in is granted again. Another process on the same
|
||||
folder, for example `vectorcraft-cli` beside the app, still reads `grants.json` as it is written. A grant for the
|
||||
same id kept in another `Plug-ins` folder counts. In Safe Mode, and for
|
||||
a stored module that the user disabled,
|
||||
same id kept in another `Plug-ins` folder counts. In Safe Mode, and for a stored module that the user disabled,
|
||||
VectorCraft does not read the module, and `grants.json` keeps the grant for its id. The Additional Plug-ins Folder
|
||||
and `plugin.reload` do not change the `Plug-ins` folder: when they leave a module that lists no permissions under an
|
||||
id, the process forgets its own grant for that id, and a grant kept in `grants.json` stays with the stored module it
|
||||
covers. An entry of `grants.json` that this version can't use (a permission it doesn't know, an invalid id) is
|
||||
skipped and stays in the file until its plug-in is granted again, replaced by a module that lists no permissions, or
|
||||
removed. When such an entry can't be removed from the file, `plugin.install` and `plugin.remove` treat it as a grant
|
||||
that can't be removed. A `grants.json` that is not a JSON list is never overwritten, and the file is never written larger than
|
||||
1 MiB, the most that is read. When a `Plug-ins` folder is in use, only the grants kept
|
||||
in it count for `plugin.install`, `plugin.inspect`, `plugin.reload` and the Additional Plug-ins Folder. At startup,
|
||||
a stored plug-in is skipped when no grant covers its bytes and the permissions it lists. The Additional Plug-ins
|
||||
Folder and `plugin.reload` install only plug-ins that such a grant covers, and report the others. A module without a
|
||||
that can't be removed. A `grants.json` that is not a JSON list is never overwritten, and the file is never written
|
||||
larger than 1 MiB, the most that is read. When a `Plug-ins` folder is in use, only the grants kept in it count for
|
||||
`plugin.install`, `plugin.inspect`, `plugin.reload` and the Additional Plug-ins Folder. At startup, a stored plug-in
|
||||
is skipped when no grant covers its bytes and the permissions it lists. The Additional Plug-ins Folder and
|
||||
`plugin.reload` install only plug-ins that such a grant covers, and report the others. A module without a
|
||||
`permissions` list, or with an empty one, needs no grant.
|
||||
- The host enforces the grant on a filter run and a command run: without `doc.read` the filter is given no objects; without `state` its
|
||||
`documentData` is `null` and an output that sets it is refused; without `doc.write` an output that changes the
|
||||
document is refused. A refused output leaves the document as it was.
|
||||
- The host enforces the grant on a filter run and a command run: without `doc.read` the filter is given no objects;
|
||||
without `state` its `documentData` is `null` and an output that sets it is refused; without `doc.write` an output that
|
||||
changes the document is refused. A refused output leaves the document as it was.
|
||||
- `api` is the plug-in API level the manifest is written for: `1` (the default) or `2`, which adds the [host
|
||||
imports](#host-imports-api-level-2), a filter's `inputs` ([reading type, images and
|
||||
symbols](#reading-type-images-and-symbols)) and a live effect's [`paintOutput`](#painted-live-effects). A higher one
|
||||
@@ -307,12 +303,11 @@ capability model proposed for PhotoCraft plug-ins
|
||||
- Parameter declarations are unchanged by permissions (`params`, below).
|
||||
|
||||
Every run gets a **fresh instance**, so no state survives between calls except what a plug-in writes to its
|
||||
[settings and cache](#settings-and-cache). Traps, exhausted budgets, malformed
|
||||
modules, bad manifests, out-of-range pointers, negative return codes and invalid output all become errors, and the
|
||||
document is left untouched: a filter's output is checked completely before one edit applies it. A live effect that
|
||||
fails draws the object's geometry unchanged; `plugin.info` reports why (`lastError`). A run that succeeds clears the
|
||||
error of a failed run. A check that finds every swatch and pattern a painted result names clears only the error of a
|
||||
refused swatch or pattern.
|
||||
[settings and cache](#settings-and-cache). Traps, exhausted budgets, malformed modules, bad manifests, out-of-range
|
||||
pointers, negative return codes and invalid output all become errors, and the document is left untouched: a filter's
|
||||
output is checked completely before one edit applies it. A live effect that fails draws the object's geometry unchanged;
|
||||
`plugin.info` reports why (`lastError`). A run that succeeds clears the error of a failed run. A check that finds every
|
||||
swatch and pattern a painted result names clears only the error of a refused swatch or pattern.
|
||||
|
||||
wasmi is built with its *portable* (loop) dispatcher: the default tail-call dispatcher relies on LLVM
|
||||
sibling-call optimisation, and when that doesn't happen a long-running module overflows the host stack, which aborts
|
||||
@@ -365,12 +360,12 @@ still returns `1`. `vc_progress`, `vc_cancelled` and `vc_log` need `progress`; t
|
||||
filter that lists `progress` runs in the background as a heavy one does, under its light budget, when it runs at once
|
||||
from Object › Plug-ins or its shortcut, or when OK runs it with Preview off; with Preview on, its dialog keeps the
|
||||
previewed run. A filter that also lists a `commandAllowlist` runs where it is called, since its command calls need
|
||||
the document. A plug-in that checks `vc_cancelled` can
|
||||
stop early, and the host discards what it returns after the Cancel or after its time budget ran out; one that
|
||||
ignores it is still stopped by the instruction and time budgets. With a known total, a report is passed on only when
|
||||
it reaches another tenth of a percent; with an unknown total, each change of `done` is passed on. After 2,000 reports
|
||||
in a run, one change in 1,000 is passed on, and so is the first report after the total becomes known or unknown. A
|
||||
live effect's progress is not shown, since it is drawn as part of something else.
|
||||
the document. A plug-in that checks `vc_cancelled` can stop early, and the host discards what it returns after the
|
||||
Cancel or after its time budget ran out; one that ignores it is still stopped by the instruction and time budgets.
|
||||
With a known total, a report is passed on only when it reaches another tenth of a percent; with an unknown total, each
|
||||
change of `done` is passed on. After 2,000 reports in a run, one change in 1,000 is passed on, and so is the first
|
||||
report after the total becomes known or unknown. A live effect's progress is not shown, since it is drawn as part of
|
||||
something else.
|
||||
- Log lines go to the app's log (target `vectorcraft_plugins`) as `<plug-in id>: <text>`, with control and
|
||||
bidirectional characters escaped. The whole text must be UTF-8. A line shows its first 1,024 bytes, as at most 1,024
|
||||
characters with the escapes, and ends with `…` when it is cut. A run writes at most 200 lines and 32 KiB of shown
|
||||
@@ -405,8 +400,7 @@ or modified time differs from the last read or write. A get answered from memory
|
||||
set to the value the key already has writes nothing, and neither does a delete of a key that has no value. A delete
|
||||
of the last setting removes the file and counts no written bytes. Every other successful set or delete writes the
|
||||
file before the call returns. A cache call reads or writes the whole entry in memory in one step, up to 64 MiB, and
|
||||
the run waits for it.
|
||||
A live effect runs while the art draws, so keep the entries it uses small.
|
||||
the run waits for it. A live effect runs while the art draws, so keep the entries it uses small.
|
||||
|
||||
- A key is 1 to 64 characters from `A-Z`, `a-z`, `0-9`, `_` and `-`, and is not a Windows device name (`CON`, `PRN`,
|
||||
`AUX`, `NUL`, `COM0` to `COM9`, `LPT0` to `LPT9`, in any case). Keys differ in case: `tile` and `Tile` are two.
|
||||
@@ -416,28 +410,27 @@ A live effect runs while the art draws, so keep the entries it uses small.
|
||||
also fails with `-4` when the `cache` folder holds more than 8,192 items; clearing the cache removes every file in
|
||||
it.
|
||||
- The desktop app and `vectorcraft-cli` keep them in a folder named from the id under `storage/` in the `Plug-ins`
|
||||
folder (ids that differ only in case get two folders): `settings.json` and a
|
||||
`cache` folder with one file per entry, named by the host. A settings file that is too large or doesn't parse is
|
||||
moved to `settings.unreadable.json`, replacing an earlier one there, and the settings start empty. A link in that
|
||||
folder is refused (`-5`). Writes go to a temporary file that then replaces the old one; a temporary file a failed
|
||||
write leaves in the `cache` folder does not count toward the caps, and clearing the cache removes it.
|
||||
folder (ids that differ only in case get two folders): `settings.json` and a `cache` folder with one file per entry,
|
||||
named by the host. A settings file that is too large or doesn't parse is moved to `settings.unreadable.json`,
|
||||
replacing an earlier one there, and the settings start empty. A link in that folder is refused (`-5`). Writes go to a
|
||||
temporary file that then replaces the old one; a temporary file a failed write leaves in the `cache` folder does not
|
||||
count toward the caps, and clearing the cache removes it.
|
||||
- A plug-in reaches only the storage of its own id. Replacing it with another module of the same id
|
||||
(`plugin.install` with `replace: true`, after the user agreed) keeps the storage; `plugin.remove` deletes it, and a run of the plug-in still going gets `-5` from every storage call after that.
|
||||
`plugin.remove` first renames the folder, adding `.removed` to its name (or `.removing` while a `.removed` folder
|
||||
holds settings an earlier removal kept). When that fails, `plugin.remove` fails and the plug-in stays installed with
|
||||
its storage as it was. Before VectorCraft deletes a renamed folder, it writes an empty file beside it with `.delete`
|
||||
added to the folder's name, and removes that file once the folder is gone. When that file can't be written, the
|
||||
folder is not deleted and is treated like any renamed folder without a `.delete` file (see below). Files that can't be deleted stay in the
|
||||
renamed folder with the `.delete` file beside it, where no plug-in reaches them. A later `plugin.remove` tries again
|
||||
to delete them when a plug-in with that id is installed or a storage folder for the id exists, and that deletion can
|
||||
fail too. For an id that isn't installed and has only such a folder, `plugin.remove` does not delete it and fails as
|
||||
not found. When `plugin.remove` can't remove the grant and can't rename the folder back either, its error names the
|
||||
renamed folder. The next load, install or `plugin.remove` of the plug-in renames a renamed folder that has no
|
||||
`.delete` file back while the plug-in has no folder of its own; when it has one, that folder is used and the renamed
|
||||
folder stays until `plugin.remove` deletes both.
|
||||
`plugin.remove` with the id of a plug-in that isn't installed deletes the storage kept for that id, when there is
|
||||
any.
|
||||
`plugin.clearCache` empties the cache and leaves the settings, and `plugin.list` and
|
||||
(`plugin.install` with `replace: true`, after the user agreed) keeps the storage; `plugin.remove` deletes it, and a
|
||||
run of the plug-in still going gets `-5` from every storage call after that. `plugin.remove` first renames the folder,
|
||||
adding `.removed` to its name (or `.removing` while a `.removed` folder holds settings an earlier removal kept). When
|
||||
that fails, `plugin.remove` fails and the plug-in stays installed with its storage as it was. Before VectorCraft
|
||||
deletes a renamed folder, it writes an empty file beside it with `.delete` added to the folder's name, and removes
|
||||
that file once the folder is gone. When that file can't be written, the folder is not deleted and is treated like any
|
||||
renamed folder without a `.delete` file (see below). Files that can't be deleted stay in the renamed folder with the
|
||||
`.delete` file beside it, where no plug-in reaches them. A later `plugin.remove` tries again to delete them when a
|
||||
plug-in with that id is installed or a storage folder for the id exists, and that deletion can fail too. For an id
|
||||
that isn't installed and has only such a folder, `plugin.remove` does not delete it and fails as not found. When
|
||||
`plugin.remove` can't remove the grant and can't rename the folder back either, its error names the renamed folder.
|
||||
The next load, install or `plugin.remove` of the plug-in renames a renamed folder that has no `.delete` file back
|
||||
while the plug-in has no folder of its own; when it has one, that folder is used and the renamed folder stays until
|
||||
`plugin.remove` deletes both. `plugin.remove` with the id of a plug-in that isn't installed deletes the storage kept
|
||||
for that id, when there is any. `plugin.clearCache` empties the cache and leaves the settings, and `plugin.list` and
|
||||
`plugin.info` give the cache's size as `cacheBytes`.
|
||||
- Without a `Plug-ins` folder (the web build, and embedders that attach none) the storage calls return `-5`.
|
||||
|
||||
@@ -458,8 +451,9 @@ has the id, `-6` when a plug-in may not call the command, `-7` when the command
|
||||
`-8` when the command ran and failed, `-9` when a limit below is passed, and `-11` when nothing can be called (from
|
||||
the module's start function, in a run that isn't a filter's run on a document, or after a call in the run failed).
|
||||
|
||||
- Every code also fails the run, whatever the plug-in does next, and the error names the call and why it failed. The run's calls and its output are one undo step, named after the plug-in or its command. A run that fails,
|
||||
traps, runs out of budget or is canceled leaves every document, its selection and its history as they were.
|
||||
- Every code also fails the run, whatever the plug-in does next, and the error names the call and why it failed. The
|
||||
run's calls and its output are one undo step, named after the plug-in or its command. A run that fails, traps, runs
|
||||
out of budget or is canceled leaves every document, its selection and its history as they were.
|
||||
- Each call runs on the document the run is for, with its params checked as any request's are, and sees what the calls
|
||||
before it changed: a `select.all` followed by `object.move` moves everything. The filter's own input is the one it
|
||||
was given when the run started; an edit still in progress (typing, or a shape being drawn) is kept as its own undo
|
||||
@@ -540,18 +534,17 @@ the module's start function, in a run that isn't a filter's run on a document, o
|
||||
no empty part between dots. A malformed list is refused when the module is installed, and the error names the id.
|
||||
- `params`: in dialog order. Names are `[A-Za-z0-9_]`, not starting with `_`, and not `id`, `ids`, `params` or
|
||||
`preview`. The host validates the user's values before calling: missing values take the default, numbers are
|
||||
clamped to `min..max` (ints rounded), a wrong type or an unknown choice is an error. Numeric defaults
|
||||
are also clamped to their range, including the implicit zero default of an `int` parameter. Besides `number`,
|
||||
`int`, `bool` and `choice` there are `string` (`{"type": "string", "default": "", "maxLength": 256}`: a text field;
|
||||
longer text is cut to `maxLength`, at most 4,096 characters) and `numberList` (`{"type": "numberList", "min": 0,
|
||||
"max": 360, "default": [45, 135], "minItems": 1, "maxItems": 16}`: numbers typed separated by commas or spaces,
|
||||
each clamped to `min..max`; a list of too few or too many is an error, and `maxItems` is at most 256) and `color`
|
||||
(`{"type": "color", "default": "#3a7bd5"}`: a color button that opens a color picker; the default, black when left
|
||||
out, and a value given to `plugin.run` may be a color object as paints hold one or a hex string, and the plug-in
|
||||
always gets a color object, its components clamped, such as `{"model": "rgb", "r": 0.23, "g": 0.48, "b": 0.84}`; a
|
||||
hex string and the picker's choice are display colors, given as the working RGB color that shows as them, so in a
|
||||
working RGB space other than sRGB the numbers differ from the hex). Hosts from before `color` refuse a manifest that
|
||||
uses it. A plug-in declares up to 256
|
||||
clamped to `min..max` (ints rounded), a wrong type or an unknown choice is an error. Numeric defaults are also clamped
|
||||
to their range, including the implicit zero default of an `int` parameter. Besides `number`, `int`, `bool` and
|
||||
`choice` there are `string` (`{"type": "string", "default": "", "maxLength": 256}`: a text field; longer text is cut
|
||||
to `maxLength`, at most 4,096 characters) and `numberList` (`{"type": "numberList", "min": 0, "max": 360, "default":
|
||||
[45, 135], "minItems": 1, "maxItems": 16}`: numbers typed separated by commas or spaces, each clamped to `min..max`; a
|
||||
list of too few or too many is an error, and `maxItems` is at most 256) and `color` (`{"type": "color", "default":
|
||||
"#3a7bd5"}`: a color button that opens a color picker; the default, black when left out, and a value given to
|
||||
`plugin.run` may be a color object as paints hold one or a hex string, and the plug-in always gets a color object, its
|
||||
components clamped, such as `{"model": "rgb", "r": 0.23, "g": 0.48, "b": 0.84}`; a hex string and the picker's choice
|
||||
are display colors, given as the working RGB color that shows as them, so in a working RGB space other than sRGB the
|
||||
numbers differ from the hex). Hosts from before `color` refuse a manifest that uses it. A plug-in declares up to 256
|
||||
parameters, at most 64 of them outside groups.
|
||||
|
||||
Each parameter may also say how dialogs show it. Hosts from before these keys ignore them, so a plug-in that
|
||||
@@ -576,17 +569,18 @@ parameters uses `label`, `unit`, `help` and `visibleWhen`, and lists the paramet
|
||||
order (bottom first): the selected ones and those inside selected groups and layers; hidden, locked, guide (compound
|
||||
guides too) and clipping paths are left out, and so are other kinds of object (type, images, symbols, live blends…),
|
||||
which a filter never touches unless its manifest lists them in `inputs` ([reading type, images and
|
||||
symbols](#reading-type-images-and-symbols)). With nothing selected the list is empty (a generator then adds its own objects). A live effect gets one
|
||||
object: its geometry (`type`, `path`, `fillRule`, `bounds`) and its `fills` and `strokes`, as a filter gets them (hosts
|
||||
from before these give geometry only). The paints are read only: an effect may draw as wide as a stroke or use a
|
||||
fill's colour to decide its shape, but it changes only the geometry, unless it declares `paintOutput` ([painted live
|
||||
effects](#painted-live-effects)).
|
||||
symbols](#reading-type-images-and-symbols)). With nothing selected the list is empty (a generator then adds its own
|
||||
objects). A live effect gets one object: its geometry (`type`, `path`, `fillRule`, `bounds`) and its `fills` and
|
||||
`strokes`, as a filter gets them (hosts from before these give geometry only). The paints are read only: an effect may
|
||||
draw as wide as a stroke or use a fill's colour to decide its shape, but it changes only the geometry, unless it
|
||||
declares `paintOutput` ([painted live effects](#painted-live-effects)).
|
||||
|
||||
An **exporter** gets something else: the native `.vectorcraft` document JSON without indentation, optional preview/PDF
|
||||
renditions or save-preference metadata (the format is documented in [`crates/format/src/lib.rs`](../crates/format/src/lib.rs)). That is every layer, object,
|
||||
appearance and effect record, artboard, swatch and symbol, so an exporter reads whatever the document holds; it checks
|
||||
`version`, since a later host may write a newer one. Images of 8 KiB or more are listed in `images` with their MIME
|
||||
type and `blob` but without their data, which follows the JSON in a saved file; smaller ones keep their base64 `data`.
|
||||
renditions or save-preference metadata (the format is documented in
|
||||
[`crates/format/src/lib.rs`](../crates/format/src/lib.rs)). That is every layer, object, appearance and effect record,
|
||||
artboard, swatch and symbol, so an exporter reads whatever the document holds; it checks `version`, since a later host
|
||||
may write a newer one. Images of 8 KiB or more are listed in `images` with their MIME type and `blob` but without their
|
||||
data, which follows the JSON in a saved file; smaller ones keep their base64 `data`.
|
||||
|
||||
An **importer** gets the bytes of the file being opened, as they are (not JSON), at most 128 MiB of them.
|
||||
|
||||
@@ -615,12 +609,11 @@ An **importer** gets the bytes of the file being opened, as they are (not JSON),
|
||||
(absent: no handle), `kind` `"Smooth"` when the handles stay collinear. Coordinates are points, y down.
|
||||
- `fills`, `strokes`: the object's fills and strokes in paint order (bottom first); `null` is no paint. A stroke has its
|
||||
`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", "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.
|
||||
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",
|
||||
"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
|
||||
`plugin.<id>` for a live-effect plug-in), `params` and `visible`. Its fills' and strokes' own effects aren't listed.
|
||||
- `bounds`: `[x0, y0, x1, y1]`, the geometric bounds (read only).
|
||||
@@ -631,9 +624,9 @@ A filter whose manifest lists `inputs` also gets the selected objects of those k
|
||||
and under the same rules (hidden and locked ones, objects that clip a clip group, alone or inside a group that clips
|
||||
it, and anything inside a symbol, a placed document or a live object are left out). They are never turned into
|
||||
outlines, rectangles or the symbol's art. Each has `id`, `type`, `name` (when it has one), `bounds` (`null` until the
|
||||
object has been laid out or drawn) and
|
||||
`transform`, `[a, b, c, d, e, f]` mapping its own space (text space, pixel space, the symbol's art) into the document.
|
||||
In this example the character and paragraph styles are cut short; a filter gets every attribute:
|
||||
object has been laid out or drawn) and `transform`, `[a, b, c, d, e, f]` mapping its own space (text space, pixel space,
|
||||
the symbol's art) into the document. In this example the character and paragraph styles are cut short; a filter gets
|
||||
every attribute:
|
||||
|
||||
```json
|
||||
{"objects": [
|
||||
@@ -662,13 +655,13 @@ In this example the character and paragraph styles are cut short; a filter gets
|
||||
`pixelsOmitted: false`. A CMYK image's pixels are its inks through the working CMYK profile, as sRGB. The pixels of
|
||||
all the images in one input take at most 16 MiB in base64, and an image the host starts to decode takes its size
|
||||
from those 16 MiB even when decoding fails. A larger image, one whose pixels don't fit what the images before
|
||||
it in paint order left, and a linked image (`linked: true`) have
|
||||
`pixelsOmitted: true` and no pixels; the host checks an image's size before it decodes the image. An image of any
|
||||
size also has `pixelsOmitted: true` when its data is missing from the document, when its width or height is 0, when
|
||||
its data cannot be decoded (including a non-CMYK image whose decoder enforces the host's 64 MiB memory limit and
|
||||
needs more, and a TIFF the host rejects as too large), or when the dimensions in its data differ from `pixelSize`.
|
||||
A plug-in checks `pixelsOmitted` before it reads `pixelsBase64`, which is present only when `pixelsOmitted` is `false`. The host never
|
||||
reads a linked image's file for a filter and never gives its path.
|
||||
it in paint order left, and a linked image (`linked: true`) have `pixelsOmitted: true` and no pixels; the host checks
|
||||
an image's size before it decodes the image. An image of any size also has `pixelsOmitted: true` when its data is
|
||||
missing from the document, when its width or height is 0, when its data cannot be decoded (including a non-CMYK image
|
||||
whose decoder enforces the host's 64 MiB memory limit and needs more, and a TIFF the host rejects as too large), or
|
||||
when the dimensions in its data differ from `pixelSize`. A plug-in checks `pixelsOmitted` before it reads
|
||||
`pixelsBase64`, which is present only when `pixelsOmitted` is `false`. The host never reads a linked image's file for
|
||||
a filter and never gives its path.
|
||||
- A symbol instance (`"symbolInstance"`): `symbolId` is the name of the symbol it shows, with `missing: true` when the
|
||||
document has no symbol of that name. `overrides` is `{}`.
|
||||
|
||||
@@ -699,16 +692,16 @@ toward the 64 MiB output cap.
|
||||
```
|
||||
|
||||
`mode` is `"filter"`, `"effect"`, `"export"`, `"import"` or `"command"` (a [command](#commands) run, whose
|
||||
`command` is the command's id); `bounds` the selection's geometric bounds (a live effect: the reference
|
||||
box of the effect, the object's bounds or those of the previous effect's result); `artboard` (filters) the artboard
|
||||
under the selection's centre, else the first; `documentData` (filters) what the plug-in last kept with the document
|
||||
(`null` until it keeps something; see Output). A live effect's `item` says where it sits: `"object"` (one of the
|
||||
object's effects), or `{"fill": i}` / `{"stroke": i}` (in the own effects of the object's `i`th fill or stroke,
|
||||
counted from the bottom as in `fills` and `strokes`). An export's `_context` is `{"mode": "export", "extension": "plt",
|
||||
"name": "Poster"}` instead: the extension of the file being written (one the exporter lists) and its name without the
|
||||
extension (the document's name when the export is not given a path), which some formats keep in their header. An
|
||||
import's `_context` is `{"mode": "import", "extension": "plt", "name": "Poster"}`: the extension of the file being
|
||||
opened and its name without the extension.
|
||||
`command` is the command's id); `bounds` the selection's geometric bounds (a live effect: the reference box of the
|
||||
effect, the object's bounds or those of the previous effect's result); `artboard` (filters) the artboard under the
|
||||
selection's centre, else the first; `documentData` (filters) what the plug-in last kept with the document (`null` until
|
||||
it keeps something; see Output). A live effect's `item` says where it sits: `"object"` (one of the object's effects), or
|
||||
`{"fill": i}` / `{"stroke": i}` (in the own effects of the object's `i`th fill or stroke, counted from the bottom as in
|
||||
`fills` and `strokes`). An export's `_context` is `{"mode": "export", "extension": "plt", "name": "Poster"}` instead:
|
||||
the extension of the file being written (one the exporter lists) and its name without the extension (the document's name
|
||||
when the export is not given a path), which some formats keep in their header. An import's `_context` is `{"mode":
|
||||
"import", "extension": "plt", "name": "Poster"}`: the extension of the file being opened and its name without the
|
||||
extension.
|
||||
|
||||
### Output
|
||||
|
||||
@@ -785,17 +778,17 @@ is a piece drawn by itself, in the order returned, with paints of its own:
|
||||
path or compound path is hit-tested on its source path and appearance. On an object with no geometry effect of its
|
||||
own, Pathfinder effects and compound shapes use the object's path for an effect on a fill or stroke, as they do for
|
||||
every other effect on a fill or stroke; on an object that has one, they also use the pieces of a painted effect on a
|
||||
fill or stroke. A 3D effect later in the stack uses the
|
||||
object's input path when the pieces name a swatch or pattern. Each piece counts against the budget of the
|
||||
object's geometry effects as well as its anchors, so all the painted effects in a stack make at most 100,000 pieces
|
||||
together; a run whose pieces would pass the budget draws its input as it was.
|
||||
fill or stroke. A 3D effect later in the stack uses the object's input path when the pieces name a swatch or pattern.
|
||||
Each piece counts against the budget of the object's geometry effects as well as its anchors, so all the painted
|
||||
effects in a stack make at most 100,000 pieces together; a run whose pieces would pass the budget draws its input as
|
||||
it was.
|
||||
- The result is checked whole before any of it is drawn: the limits of a filter's output (100,000 objects, 4,000,000
|
||||
anchors, 256 fills and 256 strokes per piece, 256 stops per gradient), finite numbers in range with nothing clamped
|
||||
(color components, tints, offsets, opacities, midpoints 0.13 to 0.87, stops in order of `offset`, stroke
|
||||
widths 0 to 1,000 pt, miter limits 1 to 500), and every swatch a solid paint, gradient or stop links to and every pattern named in
|
||||
the document. One fault refuses the whole result: that step of the stack draws its input as it was, the effect
|
||||
record stays, and `plugin.info`'s `lastError` names the plug-in and the field, for example
|
||||
`Test Tone: plug-in output: objects[1].strokes[0].width: …`.
|
||||
widths 0 to 1,000 pt, miter limits 1 to 500), and every swatch a solid paint, gradient or stop links to and every
|
||||
pattern named in the document. One fault refuses the whole result: that step of the stack draws its input as it was,
|
||||
the effect record stays, and `plugin.info`'s `lastError` names the plug-in and the field, for example `Test Tone:
|
||||
plug-in output: objects[1].strokes[0].width: …`.
|
||||
- The pieces are never written into the document while it is drawn, measured, previewed, saved or exported. PDF and SVG
|
||||
export draw them as the canvas does, from a copy of the document. Object › Expand Appearance writes each piece as an
|
||||
ordinary path or compound path with its fills and strokes, as one undo step. Object › Expand of gradient and pattern
|
||||
@@ -823,12 +816,13 @@ crates/plugins/example/build.sh # cargo build --release --target wasm32-unk
|
||||
|
||||
The script also refreshes `crates/plugins/tests/fixtures/desaturate.wasm`, the test fixture built from that source.
|
||||
The crate is not a workspace member. Any language that targets core WebAssembly works (C, Zig, AssemblyScript,
|
||||
WAT…), as long as the module imports nothing but the host imports: in Rust, `std` on `wasm32-unknown-unknown` is fine as long as it never
|
||||
touches WASI. `vectorcraft_plugins::wat::module` is the skeleton of a plug-in in WebAssembly text, which the tests
|
||||
build their plug-ins from; `wat::echo_exporter` is an exporter whose file is its input or its parameters (with a
|
||||
base64 encoder written out in WebAssembly text), which the tests read back what the host hands a plug-in with, and
|
||||
`wat::when_given` a plug-in whose output depends on whether its input or parameters hold a given text, which shows in a
|
||||
live effect's geometry. `wat::reporting` and `wat::cooperating` build level 2 plug-ins that import the host functions.
|
||||
WAT…), as long as the module imports nothing but the host imports: in Rust, `std` on `wasm32-unknown-unknown` is fine as
|
||||
long as it never touches WASI. `vectorcraft_plugins::wat::module` is the skeleton of a plug-in in WebAssembly text,
|
||||
which the tests build their plug-ins from; `wat::echo_exporter` is an exporter whose file is its input or its parameters
|
||||
(with a base64 encoder written out in WebAssembly text), which the tests read back what the host hands a plug-in with,
|
||||
and `wat::when_given` a plug-in whose output depends on whether its input or parameters hold a given text, which shows
|
||||
in a live effect's geometry. `wat::reporting` and `wat::cooperating` build level 2 plug-ins that import the host
|
||||
functions.
|
||||
|
||||
## Performance
|
||||
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ native wording review, layout checks and a web Devanagari font remain. [Notes](l
|
||||
| M7 | Type (point/area/path, editing, styles, OpenType, threading, glyphs) | Spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof and the custom dictionary and Ignore All commands and Auto Spell Check with its right-click suggestions, Ignore All and Add to Dictionary, and Spelling › Auto Spell Check in the context menu of type, and the Edit Custom Dictionary dialog (#4936; the Check Spelling dialog remains); Right-aligned area type sets the text after a line's last tab against the right edge, the text before it at its stops (#6147); A line a soft return ends is justified as a wrapped line is (#6137); Indian-language pattern hyphenation preserves syllables in both composers (#4947); 🟡 Hanging punctuation keeps its published amounts under Glyph Scaling, and Standard burasagari counts a hung line start as room; Fullwidth `!` / `?` keep an upright fallback in vertical type; Hyphenation… and Justification… per paragraph (#2310, #2755), Auto Leading also driving the Character panel's Leading, the Alt+↑/↓ leading nudge and Fill with Placeholder Text; Document-point character controls after object/group scaling (#1034; optional font-size preservation remains separate), Character/Paragraph Styles (explicit Auto-leading resets and local leading overrides), per-paragraph paragraph attributes, mixed paragraph alignment indication (headless regressions, full integration CI and visual review pass), Area Type Options (with Width/Height, Auto Size and Shrink Text to Fit; overflow reported to agents and vertical Align), type areas resized by their handles and reshaped with Direct Selection, threaded text, Fit Headline, Glyphs, OpenType panel (including font-defined Stylistic Sets), Find Font (Document, Recent and System replacement sources, #5217), installed system fonts in every font menu (fonts registered with Windows outside its font folders, the XDG and Flatpak host font folders on Linux, and fonts installed while the app runs, listed when it comes back to the front; searchable; the Character and Properties menus show each family's sample of the selected text, preview it live under the pointer or ↑/↓, show recently applied families first (#3515), filter by script and kind, list a family's styles, and star favourites; the Glyphs panel and Find Font's Replace With use the same menu without the live preview) and found by name on open/import (full-name aliases and Missing Fonts searches verified, #1258; Windows font-service QA pending), variable fonts' named instances as styles, CJK names in the UI, input methods (marked text in place, candidate window at the caret; Japanese IME), Text Wrap (offset, invert, both sides of an object; follows edits), Type on a Path effects and Options with draggable start/centre/end brackets (slide, flip), tab stops + Tabs panel (Above Text floats it over the selected text, its ruler at the canvas zoom so stops line up with the columns, #729), inline symbols in text (beyond parity), Hebrew/Arabic bidirectional text, Paragraph Direction, Every-line/Single-line Composer per object (ragged text too) done; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas tatweels on the base letter's paint (`tests::harakat`); tab leaders, spell check, vertical type pending | 45–60 |
|
||||
| M8 | Transform & distort (Puppet Warp, Liquify tools, Envelopes, Blends, Perspective Grid) | ✅ done (M8.1–M8.20 on 2026-10-06, fidelity pass): Liquify Tool Options with one shared brush, pen pressure, hold-to-apply, a safe scope and incremental strokes; tool options that last; Puppet Warp rotation, multi-select, Control bar and a rest-shape session that follows Undo; Width tool on compound paths; Envelope dialogs with preview, Reset with Warp/Mesh, full Envelope Options, type/images/symbols/appearance/gradients/patterns distorted on canvas and in every export, warps that keep their frame, Edit Contents, mesh handles and Control bar; Blend Options, anchor-targeted Blend tool, editable spine, keys picked on the canvas, faithful interpolation and knockout; Anchor Point spine conversion/handle editing and selection priority (#2544), with #2425 Shift reshape preserved; Perspective Grid with Define Grid, presets, lock/snap/rulers, widgets, the 1–4 plane switch, movable planes, Perspective Selection scale/copy/perpendicular/Transform Again, and type and symbols truly projected with Edit Text. Left: Anti-Alias and Preserve Shape output, warp-envelope point editing, brush interpolation in blends, Free Transform/Scale/Reflect along a rotated box | 2–4 |
|
||||
| M9 | Live effects (+ 3D & Materials) | 🟡 2D effects done incl. Effect → Pathfinder, Color Adjustments and Document Raster Effects Settings; PDF export warns when the pixel cap lowers raster-effect resolution; Artistic › Colored Pencil, Cutout, Dry Brush, Film Grain, Fresco, Neon Glow, Paint Daubs, Palette Knife, Poster Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting and Watercolor and Sketch › Halftone Pattern, Bas Relief, Charcoal, Conté Crayon, Photocopy, Stamp, Torn Edges and Water Paper done (57/57 Photoshop filters); Revolve with automatic smooth vector shading, default-on circular caps and continuous rims, Extrude & Bevel, Inflate and Rotate with one to ten colored directional lights and four original lighting presets; SVG Filters, remaining raster effects and 3D materials and shadows pending | 81–130 |
|
||||
| M10 | Brushes, symbols, patterns, Repeat | 🟡 pattern swatches (5 tile types, Pattern Options, editing mode, SVG `<pattern>`/PDF export) and live Repeat (radial/grid/mirror) done; interactive selected mirror axis implemented (focused and native checks passed; full CI blocked by an unrelated text test); symbol libraries done (Window › Symbol Libraries, five generated libraries (Basic Shapes, UI Frames, Map Markers, Inventory Icons, Skill Icons), User Defined, Other Library…, Save Symbols); brush libraries done (Window › Brush Libraries, six generated libraries, User Defined, Other Library…, Save Brushes); symbol editing shows draggable 9-slice guides (#5280), with guides outside the artwork still unsupported; brushes/symbols in progress | 23–37 |
|
||||
| M10 | Brushes, symbols, patterns, Repeat | 🟡 pattern swatches (5 tile types, Pattern Options, editing mode, SVG `<pattern>`/PDF export) and live Repeat (radial/grid/mirror) done; interactive selected mirror axis implemented (focused and native checks passed; full CI blocked by an unrelated text test); symbol libraries done (Window › Symbol Libraries, five generated libraries (Basic Shapes, UI Frames, Map Markers, Inventory Icons, Skill Icons), User Defined, Other Library…, Save Symbols); brush libraries done (Window › Brush Libraries, seven generated libraries including Bristle Brushes, User Defined, Other Library…, Save Brushes); symbol editing shows draggable 9-slice guides (#5280), with guides outside the artwork still unsupported; brushes/symbols in progress | 23–37 |
|
||||
| M11 | Artboards & views (selected-set deletion shared by Artboards menu, trash and Properties, retaining artboard 1 when all are selected; Artboard deletion keys keep the first of an all-selected set and protect artwork; history prunes invalid indices while preserving valid multiselection (history tests and full workspace CI pass); click-to-fit objects with the Artboard tool (#1227), artboard escape routing and document setup modal handoff done; artboard panel/tool with Move Artwork for X/Y fields and Artboard Options, Trim View, middle-button pan, print tiling and the status bar's artboard navigator with synchronized Artboard tool selection done; Window › Arrange › Tile and Consolidate All Windows done; multiple windows, presentation polish) (cached canvas reprojection preserves scale during resize, rotation and DPI changes; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) (cached canvas dimensions match the renderer limit before cache-key creation, view construction and worker submission; regression-tested; estimates unchanged); status-bar pointer coordinates follow the active ruler origin and General units | 🟡; Artboard Options shares existing presets and orientation controls and can fit artwork without moving it; artwork fitting includes all drawn document art across artboards and off-board; artwork bounds are cached by active document and revision, and selection uses the shared bounds cache across artboards | 15–25 |
|
||||
| M12 | Advanced color & art (CMYK/ICC, separations, Gradient Mesh, Live Paint, Image Trace, Graphs) | 🟡 Gradient Mesh, Live Paint, Image Trace (junction-returning pieces retain their positioned boundaries; a simpler contour walk with identical output; an edge's sub-pixel shift is the same from either side; notches read only their own piece; collapsed loops keep few segments; large images trace with a warning; Auto Color keeps rare distinct colours; quick presets trace at once and the Image Trace button opens the adjustment controls; Properties reopens them; tiny fitted contours retain islands and holes; Create Strokes: exact crop EDT, Guo–Hall thinning and maximal-disc graph simplification; 15 presets, the 14 documented ones each with an enhanced version; Ignore Color, Shapes, Auto Grouping and Gradients options; measured in `image-trace-quality.md`; closed outlines retain their interior after straightening; the Flat Logo mode places edges to a fraction of a pixel, restores sharp corners, snaps lines and circles, and refuses gradients; live trace result bounds and selection highlights exclude the retained bitmap except in source-image views), Recolor Artwork, colour management (ICC, soft proofing, separations preview), Graphs (all 9 tools, Graph Data/Type, group-selected series colours persist through regenerate/save/open) done; Graph Design and Marker designs, Transpose and Switch x/y, `\|` label line breaks, the pie options, drop shadows, the legend across the top, overlapping widths, First Row/Column in Front, filled lines, kept label formatting and the Graph Data worksheet done; graph Column designs pending; Vector Halftone Preview keeps the selection box without drawing dot selection marks (#6369); Live Paint Bucket fills faces crossed in one drag (#6373) | 6–10 |
|
||||
| M13 | Automation (Actions ✅ record/playback, persisted; variables, scripting, batch) | 🟡 MCP `add_text` exposes existing vertical point, area and path type; `script.run` and File › Scripts › Other Script… run JavaScript files, and Window › Script Console shows their output; the script document model and batch work remain | 18–27 |
|
||||
|
||||
+36
-17
File diff suppressed because one or more lines are too long
+7
-4
@@ -141,7 +141,7 @@ them all. The members stay editable: moving or editing one reshapes the solids.
|
||||
|
||||
## Clipping groups, blends and symbol instances
|
||||
|
||||
Every 3D effect (Extrude & Bevel, Extrude & Bevel (Classic), Inflate, Rotate and Revolve) also applies to a clipping
|
||||
Every 3D effect (Extrude & Bevel, Extrude & Bevel (Classic), Inflate, Rotate, Rotate (Classic), Revolve and Revolve (Classic)) also applies to a clipping
|
||||
group, a blend or a symbol instance, alone or inside a group or layer. The help page says that effects apply to
|
||||
objects, groups or layers ([Apply and modify effects](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/apply-filter-effects/apply-and-modify-effects.html)),
|
||||
that a clipping mask hides everything outside its shape ([Clipping masks](https://helpx.adobe.com/illustrator/using/clipping-masks.html)),
|
||||
@@ -279,7 +279,7 @@ Our reading:
|
||||
command is refused with "The 3D shadow is too complex; turn Shadows off or simplify the art", and a loaded
|
||||
file draws the source path.
|
||||
- Canvas, SVG and PDF exports draw the shadow, the art's bounds take it in, and Expand Appearance keeps it as a
|
||||
path below the faces. Extrude & Bevel (Classic) has no shadow.
|
||||
path below the faces. Extrude & Bevel (Classic), Revolve (Classic) and Rotate (Classic) have no shadow.
|
||||
|
||||
## Parameters (`effect.apply`, `effect.setParams`)
|
||||
|
||||
@@ -336,8 +336,11 @@ out, perspective). No Adobe software, assets, presets or bevel shapes were used.
|
||||
outline crosses. An outline of tens of thousands of points that crosses nearly every cell (a comb of 4,000 teeth
|
||||
across the shape) is refused because the 3D surface is too complex; a closed path of 65,536 points round a
|
||||
circle builds.
|
||||
- **3D (Classic)**: Extrude & Bevel (Classic) is built, with its surface types and blend steps. Revolve (Classic),
|
||||
Rotate (Classic), the track cube, map art, Turntable and 3D export are separate, unbuilt items.
|
||||
- **3D (Classic)**: Extrude & Bevel (Classic), Revolve (Classic) and Rotate (Classic) are built, with their surface
|
||||
types and blend steps. Revolve (Classic) sweeps the surface Revolve sweeps, at 64 segments for a full turn, and draws
|
||||
every face back to front, so it has no Draw Hidden Faces setting. Rotate (Classic) turns the plane Rotate turns, seen
|
||||
from either side, with the No Shading and Diffuse surfaces only. The track cube, map art, Turntable and 3D export
|
||||
are separate, unbuilt items.
|
||||
|
||||
Release measurements and a repeatable CLI scene are in [3D lighting performance](3d-lighting-performance.md).
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
# Type parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-11 · **Change:** minor (type's strokes in an envelope paint along its bent glyph outlines at their own weight, Distort Appearance on or off, as users' `.ai` files draw them, #6644; previously minor: a centred or right-aligned line of tracked type is aligned without the tracking after its last character, as the page of users' `.ai` files aligns it, #6610; previously minor: tracking and manual kerning in horizontally scaled type are thousandths of the em as the Horizontal Scale draws it, as the page of users' `.ai` files spaces such type; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: Direct Selection shows and drags the direction handles of type on a path's path, #3565; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas base-letter paint; scores and estimates unchanged; custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: right-aligned area type sets the text after a line's last tab against the right edge; a soft return in justified type no longer keeps its line from being justified; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-11 · **Change:** minor (kerning at the Type tool's caret at the start of a line sets the space before the character after it, #5111; previously minor: type's strokes in an envelope paint along its bent glyph outlines at their own weight, Distort Appearance on or off, as users' `.ai` files draw them, #6644; previously minor: a centred or right-aligned line of tracked type is aligned without the tracking after its last character, as the page of users' `.ai` files aligns it, #6610; previously minor: tracking and manual kerning in horizontally scaled type are thousandths of the em as the Horizontal Scale draws it, as the page of users' `.ai` files spaces such type; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: Direct Selection shows and drags the direction handles of type on a path's path, #3565; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; paint-only Arabic harakat keep joining with GDEF-mark ownership and Insert Kashidas base-letter paint; scores and estimates unchanged; custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: right-aligned area type sets the text after a line's last tab against the right edge; a soft return in justified type no longer keeps its line from being justified; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
Type is Illustrator's largest single area by use (weight 13 of 105 across "Type core" and "Type advanced" in
|
||||
[target-app-parity.md](target-app-parity.md)). This checklist lists the features one by one. Status is from the
|
||||
@@ -81,6 +81,7 @@ Character Styles menu New/Redefine and footer New capture the first selected cha
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-11 | minor | Kerning at the start of a line (#5111): at the Type tool's caret before the first character of the text, of a paragraph or after a forced line break, the Kerning field, Alt+arrows and Reset Tracking and Kerning set the space before the character after the caret (`CharStyle::kerning_before`, `kerningBefore` in `text.setRangeStyle`), which moves it and the rest of its line, in horizontal, right-to-left and vertical type and in SVG export. They did nothing at the text's start and kerned the invisible line break at a paragraph's. Scores and estimates unchanged. |
|
||||
| 2026-11-11 | minor | Type in envelopes: its strokes (its characters' and those on its own appearance) paint along the bent glyph outlines at their own weight, Distort Appearance on or off, as the drawn art of users' `.ai` files strokes them; with Distort Appearance they bent with the art as their filled outlines. The same holds in an envelope inside an envelope. Geometry effects on type's fills and strokes, and an opacity mask on type, still bend; Expand gives the bent outlines with the strokes on them. The visual bounds of envelopes, blends and repeats count type's character strokes as type's own visual bounds do. This changes how VectorCraft's own envelopes of stroked type draw (#6644). Score unchanged (estimated) |
|
||||
| 2026-11-11 | minor | Aligned lines of tracked type: a centred or right-aligned line is as wide as its characters without the tracking after its last one (`LineInfo::trailing`), so a centred line sits half that tracking right of where it did and a right-aligned line's last glyph ends on its anchor or frame edge, the tracking after it reaching past. The page of users' `.ai` files (used locally, never committed) aligns tracked lines so: matched glyph by glyph with the page, 176 centred lines of point type in 51 files sat half their last character's tracking left of it (median 0.500 of it; 143 within 0.05 pt), 58 right-aligned lines in 5 files the whole tracking (median 1.001), and 182 left-aligned lines where the page has them; in a line whose last character is tracked 150 and the others −34, the offset is half the last character's tracking (0.498 of it). All those lines end a paragraph. The last character is the last the line draws before a paragraph's end, a forced line break or the spaces a wrap leaves (they hang past the line). Spaces or a tab typed before a paragraph's end or a forced line break are the line's last characters, and the letter before them keeps its tracking: in the 2 such lines in the files, the page counts the space and the letter's tracking and leaves out only the space's own (VectorCraft still leaves trailing spaces out of the width). Forced line breaks and wrapped lines of tracked type aren't in the files: they take the rule unverified, so that Convert To Point Type and Artwork Cleanup's Break Text, which turn wraps into paragraphs and forced line breaks, keep the lines where they were. A hyphenated line, one whose last character hangs, justified, vertical and right-to-left lines and type on a path keep the tracking, and so do right, centre and decimal tab stops, which still align the text after them with its last character's tracking. Manual kerning on the last character, line breaking and justification are unchanged (none in the files). The tracking stays in the last glyph's advance (the caret after it and a selection of it take it in), and Fit Headline still counts it, as line breaking does. Right-aligned area type sets the text after its last tab the same way. Convert To Area Type makes the frame end where the lines are aligned, with room for that tracking, so tracked type converted either way stays where it is (it moved by the tracking on each round trip). Affinity import aligns artistic text by the same rule, so centred or right-aligned tracked artistic text moves by half or all of its last character's tracking (unverified against Affinity). Centred and right-aligned tracked type lays out differently than before in every document. Tests `tests::an_aligned_line_leaves_its_last_characters_tracking_out_of_its_width`, `tests::which_lines_leave_the_tracking_after_their_last_character_out`, `tests::an_aligned_line_leaves_out_its_last_tracking_as_glyph_scaling_sets_it`, `tests::the_text_after_the_last_tab_of_right_aligned_type_leaves_its_last_tracking_out`, and the engine's `converting_tracked_type_between_point_and_area_keeps_it_in_place` and `break_tracked_area_type_keeps_its_aligned_lines_in_place`. Scores and estimates unchanged. (#6610) |
|
||||
| 2026-11-11 | minor | Tracking and manual kerning in horizontally scaled type: they are thousandths of the em as the Horizontal Scale draws it (`vectorcraft_text::spacing_scale`), so at 50% a tracking of 100 adds 0.05 em after each character, half as much as at 100%. They were added at the full em whatever the scale, so narrow tracked type drifted further right with each letter. Illustrator's own layout in users' `.ai` files (used locally, not committed) spaces such type so, and PDF scales its character spacing by its horizontal scaling alike (ISO 32000-1 §9.4.4). Inline graphics take it after them too; vertical type keeps the full em, its advances down the column not all taking the Horizontal Scale. The automatic kerning the Character panel reads, Touch Type's limits and Artwork Cleanup's Combine Point Text measure in the same em, and Scale Text Attributes now keeps tracked type where a horizontal stretch of its transform drew it. Type that has both a Horizontal Scale other than 100% and tracking or manual kerning lays out differently than before in every document. Tests `tests::tracking_and_manual_kerning_take_the_horizontal_scale`, `tests::typo::optical_pairs_in_horizontally_scaled_type_measure_without_the_scaled_tracking`, `tests::inline::shift_tracking_and_missing_symbols`, `tests::vertical::vertical_tracking_keeps_the_em_at_any_horizontal_scale` and the engine's `horizontally_scaled_pair_reads_as_the_kerning_that_keeps_it`, `scale_text_attributes_keeps_tracked_type_where_it_was` and `combine_puts_horizontally_scaled_pieces_back_where_they_were`. Scores and estimates unchanged. |
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (Ruler origin: the corner drag snaps as a drawn point does; previously minor: Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: Outline view: images and gradient meshes are picked on what Outline draws, so a click inside them reaches the path behind, #6437; previously minor: a middle-click on a document tab closes it, #6506; previously minor: Blob Brush: a round tip's preview is the outline of the area it adds, round at both ends, as the stroke it commits (#4343); scores and estimates unchanged; previously:; ruler double-click adds a guide at the pointer, #5239; G5: Inventory Icons built-in symbol library (four built-ins); previously minor: files that record no units open in Preferences › Units › General, #5820; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; numeric fields step on wheel hover without taking keyboard focus, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Selection QoL: marquee modes and visible feedback, #5313; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Layers: Rename (`ui.rename`) opens the name field of the selected layer, group or object, #5324; Properties in Edit Artboards: Transform section above the Artboards section, #5219; scrubby labels: the Control bar's Stroke: and Opacity: links and the Properties panel's Stroke link scrub their fields, #4088; Properties: Quick Actions has Repeat Options for one selected repeat, #5181; Direct Selection: Shift-click picks several blend spine points, which drag together, #5285; Direct Selection: Cmd/Ctrl held lends the Selection tool and shows its bounding box, #3901; Join tool trims two crossing open paths and joins them at the crossing, #5844; Control bar: Paragraph: link and Align left/center/right while type is selected, #5372; shape tools: a press on the selection's centre point moves it and one in the rotate zone outside a box corner turns it, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-12 · **Change:** minor (Remove Background in the Contextual Task Bar, Properties › Quick Actions and the Control bar; previously minor: Ruler origin: the corner drag snaps as a drawn point does; previously minor: Live Paint Bucket fills faces crossed in one drag (#6373); previously: #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: Outline view: images and gradient meshes are picked on what Outline draws, so a click inside them reaches the path behind, #6437; previously minor: a middle-click on a document tab closes it, #6506; previously minor: Blob Brush: a round tip's preview is the outline of the area it adds, round at both ends, as the stroke it commits (#4343); scores and estimates unchanged; previously:; ruler double-click adds a guide at the pointer, #5239; G5: Inventory Icons built-in symbol library (four built-ins); previously minor: files that record no units open in Preferences › Units › General, #5820; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; numeric fields step on wheel hover without taking keyboard focus, #5949; Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen; Selection QoL: marquee modes and visible feedback, #5313; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Layers: Rename (`ui.rename`) opens the name field of the selected layer, group or object, #5324; Properties in Edit Artboards: Transform section above the Artboards section, #5219; scrubby labels: the Control bar's Stroke: and Opacity: links and the Properties panel's Stroke link scrub their fields, #4088; Properties: Quick Actions has Repeat Options for one selected repeat, #5181; Direct Selection: Shift-click picks several blend spine points, which drag together, #5285; Direct Selection: Cmd/Ctrl held lends the Selection tool and shows its bounding box, #3901; Join tool trims two crossing open paths and joins them at the crossing, #5844; Control bar: Paragraph: link and Align left/center/right while type is selected, #5372; shape tools: a press on the selection's centre point moves it and one in the rotate zone outside a box corner turns it, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
>
|
||||
> Vector Halftone preview update (2026-11-10): temporary dot selection marks are hidden, and the bounding box stays available (#6369).
|
||||
>
|
||||
@@ -373,6 +373,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
|
||||
|---|---|---|
|
||||
| Effect > Warp Options: style previews, orientation choices and percent controls | done | #1760; focused input, mixed-style and history checks passed; original captures reviewed. No geometry or optional Properties changes. |
|
||||
| Command search: Up and Down move through the results, Enter runs the highlighted one | done | #1798 (#1757) |
|
||||
| Learn panel: Window › Learn and Explore tutorials on the app bar open it; For you holds a Quick skill guide card, Skills lists VectorCraft's own skills with tutorial counts and empty progress rings, a tutorial shows its numbered steps, Back goes up a level | done | [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `learn.list`, `learn.show`; `panels::learn::tests` |
|
||||
| Pathfinder panel: a group selected alone counts its nested shapes, so an expanded stroked object keeps the Shape Modes enabled | done | #1642 (#1635) |
|
||||
| Selected Grid Repeat shows row, column and corner handles and spacing sliders; bounding-box handles take priority where they meet | done | #2691, `tools/src/repeat_grid.rs`; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html); drags preview and commit `object.repeat.options` as one undo step, Escape cancels; Radial Repeat has no handles yet |
|
||||
| Grid Repeat Options (Object › Repeat › Options…) has a Preview checkbox: the repeat on the canvas follows the fields while the dialog is open, Cancel puts the original back without a history step, OK keeps the previewed values as one undo step | done | #2651; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html) lists the Preview option for the grid options only, so Radial and Mirror Repeat Options have none; `dialogs/command.rs` tests |
|
||||
@@ -519,6 +520,7 @@ before release (#6373). [Paint faces and edges](https://helpx.adobe.com/illustra
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-12 | minor | Remove Background for a selected image in the Contextual Task Bar, Properties › Quick Actions and the Control bar, which runs at once with its defaults. Scores and estimates unchanged. |
|
||||
| 2026-11-12 | minor | Live Paint Bucket fills faces crossed in one drag (#6373). Scores and estimates unchanged. |
|
||||
| 2026-11-11 | minor | Ruler origin: the corner drag snaps as a drawn point does, onto the art's and the artboards' anchors, centres, edges and paths, and with Snap to Point along the ruler guides; scores and estimates unchanged |
|
||||
| 2026-11-11 | minor | Eyedropper copies effective character dimensions between transformed text objects (#6404); scores and estimates unchanged. |
|
||||
|
||||
Reference in New Issue
Block a user