The save commands' warnings now follow an export's .ai rules: a legacy
.ai file (aiVersion "7") reads only its own options, and the other .ai
and .ait files don't read outlineText, hiddenObjects, transparentImages
or preserveEditing. They name any option given beside the path that the
file doesn't read, not only compress, version, preview and precision,
and any such key in options. The bytes a save writes don't change.
The Save Options dialog sends a .ai save only the options the chosen
version reads, so a save from the dialog gets none of these warnings.
When the document's own file is already a .ai, the .ai options that a
save doesn't give and its file doesn't read keep their remembered
values, so the dialog still shows them; the save doesn't write them, and
a retry in another folder doesn't send them.
Export for Screens and Export Assets already accepted a key in a format
row or in a format's settings that only another format takes. Their
warnings now name it as given (formats[0].quality, settings.png.quality).
A key in a format's settings is named only when no row of that format,
as the export writes it, reads it. The options of an exportPreset, and
for assets.export the settings the document keeps, get no warning.
Docs: params text of document.save, document.exportForScreens and
assets.export; docs/mcp.md; docs/target-app-parity.md.
An SVG or SVGZ writes each linked JavaScript file of SVG Interactivity
into a <script xlink:href> element as stored, so a caller with
automation read roots could read a path outside them from the exported
text. With read roots in force, an SVG or SVGZ of a document that links
such a file is now refused with "a linked JavaScript file is outside the
read roots", an error that names no file or folder. No file or folder is
written, and the document keeps the link.
- The check uses the rule document.json already applies to
svg_scripts: a relative path is taken from the document's folder, a
file: URL is a file, and another URL is not checked.
- It covers document.serialize, document.export, document.exportSelection,
document.exportForScreens and assets.export (before the folder is made
or any row is written), the SVG and SVGZ saves (also a save without a
path of a document saved as SVG), file.exportAs and the SVG Options
dialog's Show Code.
- Callers without roots, and the person at the keyboard, export the
links as stored.
Docs: docs/mcp.md, docs/target-app-parity.md.
`document.export`, `document.serialize` and `document.exportSelection`
accept a key that only another format takes. `{"format": "pdf",
"quality": 80}` wrote the PDF and gave no warning about `quality`.
The reply's `warnings` now name each such key, in the wording of a
save's note: "`quality` was not used: PDF files don't read it".
- The format's own options, the commands' own keys, null values and an
exporter plug-in's format get no note.
- The note names the kind of file written. A legacy `.ai` file
(`aiVersion: "7"`) reads `outlineText`, `hiddenObjects`,
`transparentImages`, the artboard choice and `useArtboards`, and
beside `aiVersion` no other option. The other `.ai` files don't read
those three options or `preserveEditing`, which is always on for
them.
- An export doesn't read `separateArtboards`, or `range` for a
`vectorcraft` or `template` file. A save reads them to write each
artboard to a file of its own.
- In the app, an export that automation asked for (`app.export` with a
path, `file.exportAs` run as a command) gives the same notes in its
reply and in the status bar. A Background Export replies before the
file is written, and that reply has the notes. An export that a
person starts from the File menu gets none.
- Export As gives a template export no `useArtboards` or `range`: a
template doesn't read them.
- `vectorcraft-cli convert` sends `outlineText` only with
`--outline-text`, and `hiddenLayers` only to the formats that read
it. A conversion then warns only about an option the command line
gave.
`document.save` gives its notes as before: it does not apply the `.ai`
rules above.
Docs: docs/mcp.md, docs/control-protocol.md,
docs/target-app-parity.md, the params text of the three commands.
Each writer marks its notes as a loss (something of the document that the
file leaves out or that reaches it changed) or as a note on the options
(an option it didn't apply or applied another way while the art reaches
the file as it looks, or something an option turns off). The PDF, SVG,
EPS, legacy .ai, DXF and HP-GL writers return their notes on the options
in a list of their own (`setting_notes`) beside `warnings`, and the
engine's `Encoded` does the same.
The replies of the save and export commands, and of
`clipboard.exportPdf` and `clipboard.exportPng`, list the losses first,
each kind in the order the writers gave them, across every file that
Export for Screens writes. The status bar names the first note, so an
export to lossy WebP of type in a font that isn't installed now names the
fallback-font note instead of the lossy WebP note. A print to a file
keeps the order its notes are given in.
file.package, file.place, file.place.queue, file.place.info,
document.serialize, document.export, document.exportSelection and
document.exportForOffice read their switches, strings and numbers with
helpers that fell back to the default for a value of the wrong type.
file.package {"copyFonts": "false"} copied the fonts, and
document.exportForOffice {"path": 5} returned the bytes instead of
writing a file. Each such value is now an error naming the key, and
nothing is placed or written. Null still means the key was left out.
Export Selection, Export for Screens by selection or by layer, Asset
Export and CSS Properties' rasterized pictures put the objects on a new
plain layer. An object inside a clip group, or on a clipping layer, was
written without its clip, in SVG, PNG and the other formats, and the
opacity masks, effects, opacity and blend modes of the groups and
layers that held it were dropped too.
These exports now keep each object in the layers and groups that hold
it, and crop the file to the bounds the renderer measures for that art
(a clip group by its clip). Those layers are drawn even when they are
hidden, template or non-printing layers, as the objects were before. A
PSD of the export names its layers after the document's layers instead
of one layer named Selection or after the asset.
document.export with selectedOnly uses the same code: an object of a
blend, envelope, repeat or intertwine is now written on its own instead
of inside a blend of one object, and a Shaper group keeps its source
art. A text export of the selection still holds only the selected type,
not that of a type clipping path around it.
When an edit changes an object, the canvas draws again only the area
the object painted before and after it, measured by its cull bounds.
Type and groups with Extrude & Bevel, Inflate or Rotate were measured by
their letters or members, not by the solid the effect draws, which
reaches well past them. Making the solid shallower or turning it left
pieces of the old solid on the canvas outside that area until the view
was drawn again.
An object with a 3D effect is now measured by the effect's art, as the
renderer draws it: with its geometry effects, each object in it with
the raster effects of its own fills and strokes and the art its brushed
strokes draw, grown by the reach of the object's own raster effects.
One function measures that art for every bounds the renderer and
exports take, where three copies of it disagreed.
- The renderer measures the art it draws. What it makes to measure an
object it holds until the frame ends, with what the art read from
outside the object (fonts, symbols, color settings), and keeps it
with the art it draws if it draws the object while that is
unchanged. Art measured but not drawn (culled, off the view) isn't
kept. It holds at most as much as it keeps for the art it draws
(64 MB); past that, an object is made again to draw it. An object
whose 3D effect builds nothing (Revolve on a group, a solid too
complex) is measured as it is drawn otherwise, as without a renderer.
FrameStats::three_d_built counts the 3D art the renderer makes.
- The canvas measures what an edit changed through the renderer that
drew the frame shown (Renderer::changed). When it draws the part on
its own thread, it draws it with the art it made to measure it; when
the render worker draws the frame, or nothing in view changed, that
art is let go. Under Overprint Preview or Separations Preview, which
draw copies of the objects, the edit is measured afresh. An object
the frame doesn't draw (hidden, in a hidden group or layer, or
hidden by the view) is measured as nothing, before and after, so an
edit to it builds no solid: changed() without a renderer counts it as
nothing too, where it measured it. The canvas measures the edit
before it knows where the frame is drawn: when the worker draws it,
the solids it built to measure it are built again there.
- Fit to Artwork Bounds, the Artboards panel's check for it and
per-artboard SVG export measure every artboard through one renderer
(Renderer::art_bounds_on, Renderer::effect_bounds), which keeps each
object's bounds for the pass, a group holding 3D art or a hidden
member included, so each solid is built once: 30 extruded type
objects over 30 artboards take about 2 ms, where measuring their
letters took under 1 ms and building each solid for each artboard
would take 70 ms. Objects with brushed strokes, which culling leaves
room for and exports don't, are measured on each artboard. The
Artboard tool's canvas menu keeps whether Fit to Artwork Bounds has
art to fit for the document's revision, where it measured it on
every frame it was open.
- Other exports, Object › Rasterize and changed() without a renderer
build the art on each call, as 3D paths and groups already did.
Exports and Rasterize now take the solid of 3D type, where they
cropped it to the letters, and the art its brushed strokes draw, not
the room culling leaves for it. Rasterize on drawn 3D art measures it
once while the art is kept.
- A shadow after a 3D effect, on the object or on a fill after the
fill's own 3D effect, reaches past the solid once; it was counted
twice.
- With Crop Marks or Vector Halftone, which draw other art, the object
is measured by that art, as before.
- Culling leaves room for a 3D object's brushed strokes as for any
object's, and the renderer measures a 3D object again when the brush
library changes: a brush widened past that room would leave the
object culled.
A document's first frame builds the solid of every 3D object, those off
the view too, to know whether it reaches into the view: 200 extruded
words with 150 off the view take about 12 ms, where measuring their
letters took 6 ms.
Type whose object effects leave it as it is (Revolve builds no art from
type) is measured by its own box grown to hold its glyphs, as plain
type is; it was measured by its box alone, which needn't hold them.
A .ai file that File > Export As, its dialog or a control request writes
to a path now names the view's active artboard, as document.export and
document.serialize do. The UI export records the view's artboard and
takes its document from fileio::export_doc, which applies
DocState::active_artboard to the export's copy; document.export and
document.serialize use the same function.
The app now records the view's active artboard before every command,
including the commands the UI handles itself, so a .ai copy from Save a
Timestamped Copy or Export All Open Documents names it too. Before,
these files named the artboard the document opened on, or the first.
The open document is unchanged.
The editing copy of a .ai or .ait file that VectorCraft saves or exports
now has `%_<n> /Int (CropAreaActive) ,` after the ArtboardArray in its
recorded part, and marks that artboard as the selected one. The app
records its view's active artboard before each command
(DocState::active_artboard; an index past the last artboard is the last,
as in the Artboards panel); without a view, the artboard the document
opened on (Document::opened_artboard) is written, else the first. A .ai
file read from its editing copy opens on that artboard. Before, the copy
named no active artboard and always marked the first as selected.
Docs: docs/mcp.md, docs/file-format-parity.md, the editingData option text.
The Export for Screens dialog built its Example line from its fields as
typed, so it often showed a name the export does not write. It used the
artboard's name with its spaces (Artboard 1.png where the export writes
Artboard-1.png) and the first artboard whatever the range, kept a
prefix, a suffix or a device name such as CON as typed, used the scale
text as the row folder instead of the row's own folder or its size,
kept a vector row's @2x suffix, left out the folder that Create
Sub-folders adds, and read a counter set as text as 1.
The line now comes from the export's own naming. The naming part of
document.exportForScreens moves into a Naming type that the export
uses for every file, and fileio::screen_first_file returns the path of
the first file the export writes for the given params, without encoding
or writing anything. The dialog passes it the params it exports with,
without the destination, so the line shows before a folder is chosen
and is left out while the settings choose nothing to export. The Start
at field shows the counter the export uses when it was set as text.
Docs: docs/target-app-parity.md.
Save for Office Documents, the pictures that css.export rasterizes and
the PNG and PDF that Copy offers other apps were encoded with
fileio::encode, which drops the encoder's warnings. When the art held
an image that the renderer could not decode, the PNGs lacked it; type
in a font that is not installed came out in the fallback font in the
Office PNG and Copy's PNG and PDF. No reply or status message said so.
The app also dropped the warnings that document.exportSelection
returns.
document.exportForOffice, clipboard.exportPng and clipboard.exportPdf
now return `warnings`, and css.export adds the notes of its pictures to
its `warnings`, each once. Save for Office Documents and
document.exportSelection show the first note in the status bar in Save
As's message, and the CSS Properties panel's Export Selected CSS… and
Export All… show it in Export As's message. After Copy or Cut, once
the PNG and PDF are on the system clipboard, the status bar shows their
first note. The files are written as before.
Docs: docs/mcp.md (Clipboard, CSS Properties), docs/control-protocol.md.
Issue #1935 asks for batch exports of many logo variations in many
formats, each format in its own folder. This builds on Export for
Screens, which already writes every chosen artboard in every format row.
Names: a file name pattern (`name`) names every file alike. Tokens:
<filename> (the document), <name> (the artboard, asset, layer or
object), <index>, a run of # (a counter, zero-padded to the run's
length, from counterStart; counterPer item or file), YYYYMMDD,
YYYY-MM-DD, <date>, <time>, <YYYY>, <MM>, <DD>, <format>, <scale> and
<suffix>. A trailing .ext is dropped. Words are not changed: only the
date patterns and the bracketed tokens are. `date` sets the day (the
default is today, UTC). Different files that a pattern names alike get
-2, -3... Without a pattern, two rows with one name still write the
first row only, as before.
Folders: `folders` puts all files in one folder, a folder per row (as
Create Sub-folders did), per extension, per item, or per item and then
extension.
What is exported: besides artboards, assets and the full document,
`layers` (each layer's art alone, sublayers included) and `selection`
(one file of the selection, or a file per object).
Formats: rows can also be EPS, AI and VectorCraft (a document of the
item alone, its one artboard). A PDF row with printer's marks (PDF
Print in the dialog: trim, registration, colour bars, page information,
suffix -print) sits next to a plain PDF row.
Presets: document.exportPresets.list, .save and .delete keep batch
export presets with the preferences, and `exportPreset` uses one under
the call's own params. Built-in: Logo Package (SVG, PNG 1x and 2x, PDF
and print PDF, a folder per format) and Print-Ready PDF.
Dialog: Layers and Selection tabs with thumbnails; an Export Preset
menu with Save and Delete; Folders; File Name with Insert for the
tokens, Start at, Prefix, and an example of the first file's name. The
OK button says Export Layer or Export Selection on those tabs.
Tests: the pattern tokens, the request's example (SVG, PNG, PDF and
print PDF for artboards 1-3, a folder per extension, the print PDF a
bigger page), folders by item and both, the counter by item and by
file, numbered duplicates, layers and the selection, EPS, AI and
VectorCraft per artboard, presets (built-in, saved, used, replaced,
deleted, kept with the preferences), bad params, and the dialog (every
tab, the example name, a pattern export into folders, the Layers and
Selection tabs, presets applied and saved). The three tests that
refused EPS and VectorCraft rows now check those files. New labels
are translated in all 19 catalogs.
The bounds of the visible art (`vectorcraft_render::encode::art_bounds`)
are the crop of Export with Use Artboards off, EPS without artboards,
Export for Screens' Full Document and Save for Web (Legacy) with Clip to
Artboard off, and the size Fit to Artwork Bounds gives an artboard. They
measured a group or layer by its members and left out its own effects,
and measured type and images without their effects. A group's drop
shadow, or the shadow of a line of type, was cut off at the image's edge
and left outside the fitted artboard. They also counted a Shaper group's
transparent source art.
Each visible object is now measured by `vectorcraft_render::effect_bounds`,
the measure of Export Selection's crop. A group or layer without effects
of its own is still measured by its visible members, skipping a Shaper
group's source art. A group or layer with a drop shadow, a glow or another
raster effect is measured without its hidden members, guides and template
layers, unless it is a Shaper group or also has a geometry, Pathfinder,
Crop Marks or 3D effect or a visible effect on its own fill or stroke.
The SVG of a chosen artboard also keeps an object whose effects alone
reach the artboard.
Docs: docs/mcp.md, docs/target-app-parity.md.
Export Selection, Asset Export, the Assets tab of Export for Screens
and the rasterized art of CSS Properties cropped each object to its
visual bounds, which leave out live effects. A path with an 8 pt drop
shadow was exported with the shadow cut off at the path's edge, and
a group's own shadow was cut off the same way.
The crop is now the bounds the renderer measures for what it draws
(`vectorcraft_render::effect_bounds`): `painted_bounds` without its
room for brush art. A brushed path is cropped to its visual bounds as
before, and a Shaper group leaves out its transparent source art as
its visual bounds do. `assets.list` reports the same crop. The
renderer measures a shadow's or glow's reach on every side of the
object, so the image also has transparent room on the side away from
an offset shadow.
Docs: docs/mcp.md, docs/target-app-parity.md.
Every symbol instance was measured as its transform applied to the
20 × 20 square around the origin, which symbols made in the app keep
their art in. The SVG and DXF importers keep symbol art as drawn, so
the box of an instance of an imported symbol did not match its art.
The SVG Star instance at x 20, y 30 draws from (20, 30) to (40, 50)
and was measured from (10, 20) to (30, 40). Align moved such an
instance by the wrong amount, clicks and marquees beside its art
selected it, and the canvas, exports and prints left it out when its
art reached the view or the page and the square did not.
An instance is now measured by its symbol's art, which
Document::symbol_boxes looks up in the document each time
(Node::geometric_bounds_with, visual_bounds_with and
reach_bounds_with). With Use Preview Bounds on, the strokes in the art
count at their own weight, as the canvas draws them, and
object.setBounds counts them at that weight when it sizes an instance
or an object that holds one. When strokes do not scale and a width or
height of 0 or more is smaller than what a path's stroke adds,
object.setBounds now gives the path no width or height, as it does
for an instance whose art has strokes; before, it mirrored the path.
With Use Preview Bounds on, object.setBounds now measures the margins
that strokes add object by object, the members of groups and the
objects in symbol instances' art included, and solves for the width,
height and reference point asked for when the width and height are 0
or more. Before, it measured the margins of the whole selection and
could miss the size or the reference point asked for when strokes did
not scale and the objects' strokes added different margins, and when
strokes scaled and the width and height were out of proportion, even
for one path. A clip group, a Shaper Group, a compound path, type, and
a group or instance with effects or an opacity mask of its own are each
measured as one object, and so is an instance in perspective or one
turned or slanted relative to the bounding box, other than by quarter
turns. When the selection holds more than 4,096 objects to measure this
way, each selected object is measured as one. Instances inside symbol
art are measured by their own symbol's art. An instance is measured as
the square when its symbol is missing, when its symbol or a symbol
nested in its art holds an instance of itself, and when symbols nest 32
deep in its art.
Align and Distribute, the Transform panel, the bounding box and the
selection outline, clicks, the marquee, document.inspect and
document.node, Fit to Artwork Bounds and Fit to Selected Art, canvas
culling, thumbnails, the SVG filter region of an instance with a
raster effect, raster effect images, Export Selection, the SVG of an
artboard, printing, and the PDF, EPS, EMF/WMF and DXF writers use the
new box. The renderer also culls an instance by the effects and
brushed strokes in its art, and it culls a blend, envelope or repeat
by the brush art of what it holds and the effects in the art of the
instances in it. It culls an instance with geometry effects of its own
by the effects in its art after those geometry effects reshape the art.
It measures the effects in a symbol's art once for each symbol and
linear part of the transform that places it. For symbols whose art
holds instances, it measures at most 4,096 placements a frame, and at
most 4,096 more for each pattern tile it rasterizes and each placed
document it draws. Past that, it estimates the reach from the symbol's
reach with no transform, which it measures once. When the drawn
document's symbols change, the renderer drops the cached bounds of the
instances and of the objects that hold them.
The PDF and EPS writers, the SVG of an artboard and Skip Blank
Artboards grow the box of an instance whose art holds brushed strokes
by 16 times the widest stroke (at least 1 pt) of the objects in that
art that have a brushed stroke, plus 8 pt, as the renderer grows the
cull bounds of a brushed path. They keep an instance whose brush art
reaches the page. The SVG of an artboard also keeps an object that
holds an instance when what the canvas paints for it, the effects in
the symbol's art and the instance's own included, reaches the artboard.
Not yet: other commands that measure objects, such as Select › All on
Active Artboard, Transform Each and Convert to Artboards, still measure
an instance by the square.
Docs: docs/mcp.md, docs/target-app-parity.md.
- a story is read once however many text objects name it; the text objects, their pairs with the
page's type, the pieces of a line and a story's size are capped, and over a cap the layers aren't used
- the ASCII85 of the text document is bounded before it is decoded
- joining the pieces of lines that interleave no longer removes the wrong pieces
- a run length too large for the text saturates instead of overflowing
- a dictionary left open at the end of a layer, and layers nested deeper than are read, make the
file one that comes in as its page (they were silently dropped)
- a file whose editing data can't be decoded says so, and the notes about art that was too much or
too far away count as losses, so writing over the source asks first
- the private data of a .ai is inflated under a cap of its own (the PDF reader's decoder has none)
- the other writers (PDF, selection, office, print) ask before replacing a source that lost things,
and a recovered copy remembers that
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An Illustrator EPS is a flattened page plus, after %%EOF, an editing copy of
the document; a PDF-compatible .ai carries the same in its private data.
Importing only the page lost layer names, order, visibility, hidden layers
and hidden objects and hidden text.
- eps: read the editing copy (Zstandard %AI24_DataStream, older zlib
%AI9_DataStream) into layers, sublayers, groups, compound paths, clip
groups and hidden objects, run by the existing PostScript interpreter.
Falls back to the page, with a warning, when a shown layer uses an
operator that isn't read or the art differs from the page by over 5%.
- eps: read the text document (ate.rs) to place type on hidden layers and
objects; the page's type is matched to the file's text objects by
position (pieces of a line included) and kept with the nearest one when
none claims it. Area type and type on a path are left out with a note.
- pdf/engine: .ai files go through the same reader.
- engine/cli: a file whose import left things out isn't overwritten unless
asked (acknowledgeLoss, --replace-lossy).
- eps: font names re-encoded with a *<n> suffix resolve to their family.
- i18n: es/it/ru for the new import messages; ROADMAP updated.
Container and text-document formats were inferred from user-owned files; the
Adobe file format specification was not read.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Exporting one artboard of many to SVG wrote the whole document's art into
that artboard's file, the other artboards' art outside its viewBox: an
editor opening it showed every artboard's art. A file for an artboard
named in the export (Use Artboards with a range or All, Export for
Screens) now holds the objects over that artboard only, at any depth of
layers, as DXF's crop leaves them; objects reaching into it stay whole
and a clipping layer keeps all of its objects. An SVG with no artboard
named (Save As) still holds the whole document.
Part of #546: Export As with a range writes the chosen artboard alone in
SVG, DXF, PDF and PNG, each file with that artboard's art (tested from
the dialogs).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.exportSelection (the MCP export tool's `selection: true`) now
replies with `format` and the encoder's `warnings`, the same list a
whole-document export gives: lossy WebP written lossless, blend modes left
out of SVG Tiny 1.2, PDF layers at 1.4 and the rest. A selection export
that approximates nothing replies with an empty list.
Root cause: export_selection encoded with `encode`, which drops the
warnings encode_with_warnings returns, and its reply carried only path,
bytes and bounds. The params doc and docs/mcp.md now describe the reply.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
compatibility "1.3" writes a %PDF-1.3 file (its XMP metadata says 1.3
too) without transparency: a copy of the document is flattened first
with flattenerPreset (stored with PDF presets; empty is High Resolution)
and the flattener options over it, see-through image pixels counting as
transparency and rasterized areas clipped to their regions so no soft
mask is needed. Page groups are left out and the writer refuses a file
that would still have transparency. PDF/X-1a and PDF/X-3 share this
path (their version is now PDF 1.3; 1.4 is still accepted and written
as 1.3), and with it the preset, the header and metadata code and the
transparency check. Passwords at PDF 1.3 encrypt with 40-bit RC4
(revision 2). The Save PDF dialog lists PDF 1.3 and picks the flattener
preset under Advanced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The svg crate's new CSS writer (css_rules) gives the CSS web pages style objects
with: rectangles and ellipses as background-color / linear-/radial-gradient(),
border and border-radius; type as font, color, spacing and alignment; shadows,
glows and blur as box-shadow, text-shadow and filter. It shares SVG export's
declarations (transparency, fonts, type spacing, colours, gradient stops), which
moved into svg/src/css.rs. Options: units, absolute position, width and height,
unnamed objects, rasterize unsupported art (as a PNG background-image).
Commands css.selection, css.generate and css.export; UI commands css.copy and
css.exportFile; the CSS Properties panel with a highlighted code view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Window > Asset Export lists the document's assets as thumbnails: art
dragged in off the canvas (or added with +) becomes one asset per object,
or one of them all with Alt; Object > Collect for Export does the same.
Assets name their objects, so they follow edits (moving the art moves the
crop) and deleting the art removes it from its asset. Click selects,
double-click renames, the bin removes. The panel's export settings are
Export for Screens' own format rows on the document's shared settings, and
Export writes each asset alone, cropped to its art, into a picked folder
(the web downloads a zip). Export for Screens' Assets tab lists the assets
with checkboxes and exports the checked ones.
Commands: assets.add, assets.remove, assets.rename, assets.list,
assets.export, assets.settings.set; document.exportForScreens takes
assets. Model: Document.assets (ExportAsset {id, name, nodes}).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New crates/eps (L3): a hand-written EPS writer. A DSC header with
%%BoundingBox and %%HiResBoundingBox (y up from the origin), %%LanguageLevel
and spot colours; paths filled and stroked with caps, joins, miter limits
and dashes (strokes a plain line can't draw as filled outlines, brushes as
their art), clipping groups, linear and radial gradients as smooth
shadings at Level 3 and stepped fills at Level 2 or with compatible
gradient printing, patterns as tiles, freeform gradients and placed images
as images (Flate with masked transparent pixels at Level 3, run-length at
Level 2), type as glyph outlines, spot colours as Separation spaces and
overprints when preserved; RGB written as CMYK on request. A TIFF preview
(1-bit or colour, transparent or opaque) behind the binary header, a PNG
thumbnail and the native document in comments (eps::native and
eps::thumbnail read them back for a later import).
document.export {format: eps} and document.exportEps: transparency is
flattened first with a flattener preset (built-in or saved) and options;
the visible art's bounds, or with useArtboards one file per artboard named
{stem}_{artboard}.eps. Export As lists EPS, then EPS Options
(ui.epsOptionsDialog for agents, remembered between exports).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SVG export writes UTF-8, UTF-16 (big-endian with a byte order mark) or
ISO 8859-1 (other characters as character references) and SVG/SVGZ
open in any of them. The Tiny 1.2 profile (simplified) writes
presentation attributes only and leaves out filters, masks, blend
modes and symbols with warnings. embedFonts embeds the fonts type uses
as @font-face subsets of the characters used, honouring the fonts'
fsType (whole when subsetting is forbidden, left out when embedding
is). New options: encoding, profile, embedFonts (SVG Options dialog,
document.formats); document.serialize also returns dataBase64 for
non-UTF-8 files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.exportForScreens takes zip: true to pack every file into one
store-only .zip (returned as dataBase64 without a folder, written there
with one); without a folder the files come back as base64. The engine's
Export for Screens moves to fileio/screens.rs, with a small ZIP writer
(fileio/zip.rs).
The dialog starts in the user's Desktop (else home, on every OS), has a
folder picker button (the Services.pick_folder the Links panel uses) and
stays open when the export fails. On the web it has no folder: OK reads
"Download" (DialogSpec.ok_label) and the files download, several as one
zip. file.exportForScreens with params goes through io::export_for_screens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New crates/cad (L3): a hand-written ASCII DXF writer. Layers become DXF
layers (hidden: off, locked, non-printing: not plotted), straight paths
LWPOLYLINE (POLYLINE before R14), curves cubic SPLINEs through every
anchor, fills solid HATCHes (outlines in R12), strokes lines with a
lineweight and a linetype per dash pattern, type TEXT or glyph outlines,
placed images IMAGE entities linked to PNG or JPEG files. Units and
scale ($INSUNITS), scaled lineweights, 8/16/256 indexed or true colour
(420), transparency (440), Preserve Appearance / Maximize Editability,
Alter Paths for Appearance, Outline Text.
document.export {format: dxf} and document.exportDxf; useArtboards
writes one drawing per artboard; selectedOnly (any export) writes the
selection in its layers. Export As lists DXF (then DXF Options,
ui.dxfOptionsDialog for agents, remembered between exports) and DWG
greyed out; document.formats lists DWG and PICT as unsupported with a
hint, and exporting or opening them answers with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- WebP takes lossless and quality; there is no permissive pure-Rust lossy encoder yet, so
lossless: false writes lossless with a warning (the WebP Options dialog says so).
- document.export {format: "txt", encoding: utf8|utf16, lineEndings: lf|crlf,
selectionOnly}: the stories in stacking order (back to front), a thread once, hidden
objects and template layers left out; UTF-16 is little-endian with a byte order mark.
Export As → Text opens Text Export Options.
- File → Save for Office Documents… (document.exportForOffice {ppi, transparent, artboard,
path}): the artboard as a PNG at 72/150/300 ppi on white or transparent, through a small
dialog in the app.
document.formats' writable list and the MCP export tool's enum gain txt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.export {format: "png8" | "gif"} reduces the art to a palette of at most `colors`
(2–256) entries: median cut over a 15-bit histogram refined by k-means (perceptual,
selective or adaptive weighting), or the web-safe, black-and-white or grey palettes; art
with that many colours or fewer keeps them exactly. Dithering is Floyd–Steinberg diffusion,
an 8×8 ordered pattern or noise, with an amount. Pixels under half opacity become one
transparent entry; partly transparent edges blend over the matte. PNG-8 is an indexed PNG
(PLTE, tRNS, 1–8 bits per pixel, Adam7); GIF is a GIF89a (optionally interlaced). Both are in
document.formats, Export As (PNG-8 / GIF Options dialogs) and Export for Screens.
document.formats' writable list and the MCP export tool's format enum gain gif and png8
(their tests list them).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.export {format: "jpg"} takes colorModel (rgb|cmyk|gray), quality 0–100, method
(baseline|optimized|progressive), scans (3–5), embedIcc and imageMap (none|client|server).
JPEGs are written with jpeg-encoder. CMYK files hold ink amounts drawn as the renderer's ink
planes, so CMYK colours keep their inks (black type stays black ink alone). Profiles are made
in code from the CMS (cms::icc_bytes): the RGB spaces from their primaries, the CMYK spaces as
Lab lookup tables sampled from their model, a grey with sRGB's tone curve, and loaded
profiles as loaded. Image maps write <stem>.html or <stem>.map beside the image for objects
with a URL and an Image Map shape. The JPEG Options dialog gains Color Model, Quality 0–10,
Method and Scans, Image Map and Embed ICC Profile; CMYK documents default to CMYK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Save As offers VectorCraft, VectorCraft Template, PDF, SVG and SVGZ and asks
for the chosen format's options before writing: SVG Options for SVG/SVGZ, the
Save PDF dialog for PDF (its OK finishes the save); on the web a Save As
dialog first names the file and picks the format. A document opened from or
saved as SVG/PDF remembers its format and options in one place
(DocState.format, save_options: the SVG options, or every Save PDF setting),
so Save writes it again; the result's warnings name what the format loses,
then the encoder's notes. Save a Copy writes "<name> copy.<ext>" and leaves
the path, title and modified state alone. Save is off for a clean saved
document, and Save as PDF... is in the File menu.
Engine: document.save, file.saveAs and file.saveCopy take {path?, format?,
options?, svg?} (SVG options also flat) and write linked images beside an
SVG; file.formatOptions reads a format's options. save_plan/save_with are the
one save path of every frontend, on the shared SVG encoder (one SVGZ path).
The template format is writable. Save panels get per-format filters, a folder
and a suggested name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve Editing (on in the VectorCraft Default preset) embeds the
native document in the PDF as an embedded file with a hash of the pages
it was written with. document.open of a PDF, .ai or .ait restores it
exactly (restored: true) and falls back to importing the artwork, with a
first warning saying why, when the pages were changed elsewhere or the
data can't be read; SVG and PDF share that fallback and its warnings.
The legacy drawcraft payload name is read too. vectorcraft_pdf gains
editing(), ImportReport.native and PdfOptions.native.
document.save / file.saveAs / Save a Copy write .ai (format "ai" or an
.ai path): every artboard as a PDF that always carries the document and
takes the PDF options; a restored .ai keeps its path so Save writes .ai
again, and .ait templates open their document untitled. The desktop
save dialog lists the formats Save writes. Choosing a standard turns
Preserve Editing off (PDF/A refuses it), Export for Screens and PDFs of
some artboards leave it out with a warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File > Export > Export As... (file.exportAs) picks any writable format and Use Artboards: All
or a range writes one file per artboard, <file>-<artboard>.<ext> (a PDF keeps them as pages);
off, the export covers the bounds of the visible art (an artless document is refused before the
save dialog). The format's options dialog follows: PNG/JPEG/WebP Options, SVG Options, or Save
PDF for artboards as pages. document.export takes useArtboards for raster formats (true: one
file per chosen artboard, default all) and PDF (false: the art's bounds), and the app's exports
return every file written and the encoder's warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PNG is written by an in-code chunk writer with the resolution in pHYs and optional Adam7
interlacing (JPEG stores the resolution in JFIF). document.export takes ppi (72/150/300/other,
wins over scale), background (transparent, white, black or a colour) and antiAlias (none, art,
type: text snapped to whole pixels); Export for Screens rows take them too (plus one top-level
antiAlias, and a row's ppi names its @Nx suffix), and so does object.rasterize. Hard edges stay
hard in every offscreen pass (groups, masks, filters), whatever the thread count.
RenderOptions.anti_alias and render::encode::RasterExportOptions; PNG/JPEG/WebP Options dialog
(pngOptions/jpgOptions/webpOptions) after File > Export As PNG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
svgz is a writable format with the SVG options (document.export,
serialize, save, Export for Screens): exactly the SVG export, gzipped.
SVGZ is recognised by its extension or the gzip magic bytes and opens,
places and pastes like SVG (clipboard.importSvg takes dataBase64 of SVG
or SVGZ); Export As SVG or Save As to a .svgz name writes it
compressed. Gzip lives in the svg crate (pure-Rust flate2, builds for
wasm) and unpacking stops at 512 MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.export, serialize and save, and the Save As / Save a Copy UI
commands, take the SVG options flat or as svg: {…}: styling
(presentation, style, entities, css), outlineText, images (embed or
link; linked files are written next to the SVG), objectIds (layerNames,
minimal, unique), decimals 1–7, minify, responsive, useArtboards and
range "all" (one SVG per artboard), preserveEditing (the SVG reopens as
the full document) and metadata. Unknown keys are rejected and
document.formats lists every option with its default. Results carry the
SVG writer's warnings.
Save writes SVG to an .svg path and Save again reuses its options.
Export As SVG and Save As / Save a Copy to .svg open SVG Options (first
use: Internal CSS and Responsive, then the last-used choices) with a
read-only code view. Save a Copy no longer retargets the document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
document.export and document.serialize return the encoder's warnings (PDF:
options not applied yet, features approximated or left out, overprint).
document.pdfSettings lists the options that differ from the defaults and
the warnings (includeDocument adds the active document's own from an
in-memory export); the dialog's Summary section shows both. View PDF after
Saving opens the written file as a file:// URL through Services.open_url;
the status line reports the notes. The PDF writer's raster-effects warning
now names what it leaves out (objects of a kind, fills, strokes) instead of
claiming raster effects are never exported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>