A plug-in could filter objects or reshape them as a live effect, but
nothing it made could leave the app: pen plotters, cutting machines and
laser cutters read formats VectorCraft doesn't write. An exporter is a
third kind of plug-in ("kind": "exporter", with the file "extensions"
it writes), in the same sandbox and with the same ABI v1 exports.
- It adds a write-only format, plugin.<id>. document.formats lists it
with its plug-in id, and document.export writes it by that id, or by
a path ending in one of its extensions that no built-in format has;
"params" carries its parameters, checked against its manifest as a
filter's are.
- File › Export › Export As lists it after the built-in formats, with
Use Artboards greyed out, since the plug-in writes the whole
document. One with parameters asks for them in a dialog generated
from its schema (pluginExport) once the file is picked.
- The plug-in is given the native file's JSON (format::json_part:
images of 8 KiB or more without their data) and _context {mode:
"export", extension, name}. It returns {"dataBase64", "warnings"?}
or {"error"}. The host writes the file, and nothing when the plug-in
declines or returns bad data.
- An export gets 10 G instructions plus 4,000 per byte and 300 s.
Background Export's Cancel stops it between two slices of that
budget (Error::Cancelled), and a cancelled live-effect run is not
cached as a failure.
- plugin.run and Object › Plug-ins say where an exporter is instead of
calling it a live effect.
engine/tests/plugin_export.rs reads back what an exporter is handed
with wat::echo_exporter, an exporter whose file is its input or its
parameters (its base64 encoder is written in WAT). The document it is
given is document.save's JSON, without the large image's data, and its
parameters arrive defaulted, clamped and with the file's name and
extension. It also covers listing and removal, built-in extensions
winning, selectedOnly, declines, bad base64, missing data, warnings,
and Cancel stopping a plug-in that never returns. It is a test binary
of its own: installed plug-ins are process-wide, and the format-list
unit tests check exact lists. dialogs/tests_plugin_export.rs drives
Export As headlessly; plugins/tests/abi.rs covers the budget, the
parameters and the echo exporter; format/tests/native_v4.rs covers
json_part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
Move the Preserve Editing check out of save.rs into svg.rs, where it
reads the options through `plan` as the encoder does; SaveJob::encode
still puts the warning first. Review follow-up on #2994.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A document with more than one artboard saved as SVG/SVGZ without
Preserve Editing Capabilities is written as one artboard and reopens
with one, so the others were lost without a word. The save now says so
(first, since the status bar shows only the first warning) and names the
two ways to keep them all.
Refs storytold/vectorcraft#2091
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A CMYK JPEG came out with JFIF's APP0 segment at its head and four components
in the frame:
APP0/JFIF APP14/Adobe APP2[ICC_PROFILE 1/4 … 4/4] SOF0: 4 components
JFIF describes one- and three-component images, greyscale and YCbCr, so an APP0
segment in front of a four-component frame says two different things about the
file's colours: a reader that trusts JFIF decodes nonsense, and one that checks
refuses the file. Adobe's own CMYK JPEGs carry the APP14 marker the encoder
already writes, and no JFIF. `jpeg-encoder` writes JFIF unconditionally, so the
segment is replaced here.
The resolution JFIF carried moves to an EXIF APP1 segment -- `XResolution`,
`YResolution` and `ResolutionUnit` -- which is where `cmd::fileio::ppi` already
looks when a file has no JFIF, so nothing is lost; with no resolution asked for
the segment is simply dropped. RGB and greyscale files keep JFIF, which is what
it is for.
Found while looking at #3084, whose default RGB exports are valid here; this is
the file that is not.
Honor the requested text mode in the single-page importer, as Open does, instead of forcing outlines for Place and PDF clipboard import.
Add synthetic regressions for bytes, unlinked paths, selected pages and explicit outlines. Update format parity and milestone evidence without changing scores.
document.pdfInfo read thumbnail with as_u64, so a value such as -1,
1.5, "1" or true gave a reply without a thumbnail and no error. This
happened for a PDF and for a PostScript file.
A thumbnail that is present and not null must now be a whole number
from 1 to the page count. Any other value is the existing "thumbnail:
no page {n} (the PDF has {count})" error, or "(the file has 1)" for
PostScript, with the value as given. An absent or null thumbnail still
gives no thumbnail.
PNG, PDF, EPS, legacy `.ai`, packaged zips and saved `.vectorcraft` files
record the wall-clock time they were made in, so the same commands run twice
gave different bytes although the pixels matched (#2880):
PDF /CreationDate and /ModDate D:20261018155816 vs D:20261018155817
PNG the `Creation Time` tEXt entry
.vectorcraft one byte of the stored date
SVG carries no time, and already repeated
`metadata::output_time` is the time a file records: `SOURCE_DATE_EPOCH` when it
holds a non-negative whole number of seconds, else the clock, as the
reproducible-builds specification defines it. A date a command is given
(`file.new {created}`, a save's `modified`) still wins over the variable, and a
blank, negative or non-numeric value is ignored so that an export is never
refused over the environment.
`metadata::now_unix` keeps the real clock and now has exactly two callers, both
of which must not be pinnable from the environment: the recovery heartbeat, and
the seed PDF encryption draws on.
A .ai saved without PDF compatibility has only a placeholder page in
its PDF part. document.open reads such a file from its editing data,
but with `pages` given it failed with "the file holds only a
placeholder page", even for "1" on a file of one page.
When `pages` names every page of the file, document.open now reads
the editing data as it does without `pages`.
Docs: docs/mcp.md.
A PDF or PDF-compatible .ai saved with Preserve Editing carries the
native document as an embedded file, with a hash of its pages at the
end of the file's /Desc. The import read /Desc with from_utf8_lossy and
looked for "hash ". /Desc is a PDF text string, which may be written as
UTF-16BE with a byte order mark (ISO 32000-1 7.9.2.2). Written that
way it holds no UTF-8 "hash ", so the import found no hash, took the
editing data as intact and reopened the native document with no
warning, even after another app had changed the pages.
/Desc is now read with text_string, the importer's decoder for text
strings, so the hash is found in UTF-16BE, UTF-8 and PDFDocEncoding.
Changed pages open as artwork with the "changed in another app"
warning. The embedded file's /UF and /F names are decoded the same
way, so editing data named only in UTF-16BE is found.
A /Desc without a hash is still trusted, as before.
craft-docpath follows a network path only on the share the document is
on, and its caller contract asks for a document opened from a mapped
drive to be passed by its UNC path. fileio::document_path passed the
drive path as given, so a document opened as Z:\jobs\doc.vectorcraft
treated a link stored as \\server\share\img\logo.png, on the share
behind Z:, as on another computer: the image was missing, and
document.open, links.check and links.info gave refused: "the path is on
another computer".
On Windows, when a stored path is refused as on another computer and
the document is on a drive, document_path now reads the final path of
the document's folder (std::fs::canonicalize). When that names a share
(\\?\UNC\server\share\...), the path is checked again against the
document's path on that share. Links on other computers stay refused,
and paths that were followed resolve as before. Opening a document,
Link Info, links.check and the linked-file watch all use it. Other
systems are unchanged.
Docs: docs/mcp.md, docs/target-app-parity.md.
document.pdfInfo refused PostScript (an EPS, a .ps file, or a .ai in
PostScript form), mostly with "this is a PostScript file ..., not a
PDF", although document.open and file.place read the same file
through the EPS reader as one page.
It now answers {pages: 1, needsPassword: false, pageInfo: [{width,
height, rotation: 0, boxes: {}}]}. The page is the file's bounding box
(%%HiResBoundingBox when it has one, a US Letter page without one),
read from its comments without running its program. thumbnail: 1 is a
PNG of what that page prints, read by the EPS reader without any
editing data the file carries. A program whose header is only %! is
read as PostScript, as clipboard.importEps reads it.
Docs: docs/mcp.md, docs/target-app-parity.md.
The PostScript interpreter that reads EPS and PostScript .ai files
accepts a FlateDecode or LZWDecode filter with Predictor 2 at
BitsPerComponent 1, 2, 4, 8 and 16, but undid the horizontal
differencing only at 8 bits. At the other depths the filter gave back
the stored differences, and the image drew them instead of its
samples.
The predictor is now undone at every depth it accepts, as ISO 32000-1
§7.4.4.4 describes: each component adds the same component of the
pixel to its left, within its bit width, and each row starts again.
16-bit components add as big-endian 16-bit numbers. The padding bits
at the end of a row stay as they are, and 8-bit data takes the same
path as before. The PNG predictors are unchanged.
Docs: docs/target-app-parity.md.
While an export runs in the background (Background Export), a dialog shows
what is being exported and how far it has got: artboard by artboard for a
raster export of several artboards, busy otherwise. Cancel (also Esc) stops
it and writes no file; Continue in Background leaves it to the status bar.
It opens after 0.4 s and never over another dialog.
Built on the shared vectorcraft_doc::cancel (#2546): fileio::progress::Progress
wraps its CancelToken and a progress sink, so a cancelled export stops
drawing and keeps no half-drawn pattern tile or effect raster.
- ui: background jobs carry their Progress; export_progress draws the
dialog; UI commands file.cancelExport and file.exportInBackground.
- i18n: the strings in every language with complete menus, Danish, Dutch
and Indonesian included; "{n} more exports waiting" is a plural (trn).
- docs: ui-parity, gaps (G12) and ROADMAP rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dt49z1ubK9x1bPq51APgz3
A .ai can keep its variables and data sets in a variable library in
its editing data. VectorCraft doesn't read that library, and such a
file opened with an empty Variables panel and no warning about it.
The reader now counts the library's variables and data sets in the
editing data's /Document dictionary, and opening the file adds a
warning: "the file's variables and data sets weren't read (2
variables, 3 data sets): the Variables panel doesn't list them". The
same warning appears when the layers can't be read and the file opens
from its PDF part. A PDF that opens from the editing data it carries
gets the warning too. A library with no variables and no data sets
gives no warning.
Docs: docs/target-app-parity.md.
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.
The engine built the PDF options without a creation date, so the
writer dated every PDF and PDF-compatible .ai file with the time it
was written. A document created on 2020-01-01 (File › File Info
shows the date) exported as a PDF with today's date in /CreationDate
and xmp:CreateDate. SVG export with the metadata option already
writes the document's own date.
The options now carry the document's File Info created date and the
time of writing as the modification date. A document without a
creation date is dated with the time of writing, as before, so PDF/X
files still get their required date. The PDF writer writes one date
for both, so the export then sets /ModDate, xmp:ModifyDate and the
PDF/A history dates to the time of writing; /CreationDate and
xmp:CreateDate keep the document's date. The page information mark
and the embedded editing data's modification date keep the time of
writing.
Docs: docs/mcp.md (File Info), docs/target-app-parity.md.
file.saveAsTemplate with format ait and aiVersion: "7" wrote the
legacy PostScript .ai of version 7 under the .ait name: for a
one-artboard test document, a 7,556-byte file starting %!PS-Adobe-3.0.
The web's Save as Template dialog showed the .ai Version menu for
.ait, whose Legacy (version 7) choice set aiVersion 7.
aiVersion 7 for a .ait is now an error, "aiVersion 7 writes legacy .ai
files; a .ait template is always PDF-compatible", and nothing is
written. The 17 catalogs that translate every message have a row for
it. The dialog shows no Version row for .ait, and aiVersion "pdf"
still writes the PDF-compatible template.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/file-format-parity.md
(two stale rows for .ai and .ait removed).
document.export, document.serialize, document.exportSelection and
Export for Screens rows answered "PDF-compatible .ait template files
can be opened but not written" for a .ait, although
file.saveAsTemplate writes them. The CLI's convert and run --export
call document.export, so vectorcraft-cli convert in.pdf out.ait
failed with that error.
The error now reads "PDF-compatible .ait template files are written
by file.saveAsTemplate", with a row in the 17 catalogs that translate
status messages and in bg, da, id and nl. convert and run --export
write a .ait with file.saveAsTemplate, which writes every artboard
with the .ai options. convert passes --replace-lossy and its .ai flags
on, and refuses --artboard and --range for a .ait.
Docs: docs/mcp.md, docs/target-app-parity.md.
When the PDF writer keeps a PNG, GIF or WebP image as it is, it accepts
the image once its header reads, and it reports pixel data that it
can't decode only when it finishes the file. An image whose pixel data
was cut short or damaged therefore failed the whole export with "PDF
writer error: Image(...)", and nothing was written. This happened in
Save as PDF, .ai saves, Save a Copy, Print, Export for Screens and
native files with PDF compatibility, and with On Copy: PDF turned on,
Copy left the PDF off the clipboard.
When the writer fails on an image's pixel data, the export now runs
again with that image left out. Its warnings hold the note that raster
exports give, once: "an image couldn't be decoded and is missing from
the picture". This note replaces the writer's "image '<key>' could not
be decoded and was skipped", which named the image by an internal key.
An export without such an image runs once, as before. A .ai file keeps
the image in its editing data and reopens with it.
Docs: docs/mcp.md (PDF options).
Saving a version 7 .ai expands raster effects into images and flattens
images with see-through pixels, opacity or a blending mode, and both
draw the art with the renderer. An image the renderer couldn't decode
drew nothing, so it was missing from the result, and the reply carried
no note. A semi-transparent image cut short was removed without one.
The save now compares decode_failures() before and after it writes the
artboards, as Expand Appearance and the raster exports do, and adds
"an image couldn't be decoded and is missing from the picture" to its
warnings once.
Docs: docs/mcp.md (aiVersion "7").
A PDF-compatible .ai, or a PDF saved with the editing data, whose
editing data can't be decompressed (damaged zlib or Zstandard data, or
data that decompresses to more than 256 MB) opened from its PDF part
with no note about its layers. layered_ai returned on the
decompression error without a note. Data that decompresses to layers
that can't be read gets one, and so does an EPS.
layered_ai now gives the same note with the decompression error as
its reason: "the file's layers weren't read from its editing data (its
zlib compression is damaged): they come from its PDF part". The note
counts as a loss, so document.export or Save As over a PDF saved with
the editing data asks first (acknowledgeLoss), as it already did for
a .ai. The three reasons have rows in the 17 catalogs that translate
every message.
Docs: docs/mcp.md, docs/target-app-parity.md.
The layers of PDF and PDF-compatible .ai files come from their
optional content groups, whose /Name is a PDF text string. The import
read a name without a byte order mark as Latin-1. Such a string is
PDFDocEncoding (ISO 32000-1 Annex D, Table D.2), which differs from
Latin-1 at 0x18 to 0x1F and 0x80 to 0xA0. A group named
(Art\205final) opened as a layer named "Art", U+0085, "final" instead
of "Art–final", and curly double quotes (0x8D, 0x8E) and the euro sign
(0xA0) became control characters and a no-break space.
text_string now reads such a string with the PDFDocEncoding table that
File Info already used for the document information dictionary. The
table moves from file_info.rs to import_scan.rs, and File Info reads
the information dictionary's strings with text_string.
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.
Export for Screens wrote each file with fileio::encode, which drops the
encoder's warnings, and its replies had no `warnings` key. When the art
held an image that the renderer could not decode, or type in a font
that is not installed, the files lacked the image or used the fallback
font, and neither the reply nor the status bar said so. document.export
returns the same notes in `warnings`, and Export As shows the first in
the status bar.
document.exportForScreens and assets.export now collect the warnings of
every file they export, each once, and every reply carries them in
`warnings`. The export still writes or returns every file. When there
are warnings, the Export for Screens dialog and the Asset Export panel
show the first in the status bar as Save As shows a save's notes.
Docs: docs/mcp.md (Export for Screens), docs/control-protocol.md.
Export for Screens, Batch over data sets, Save for Web and the
Libraries panel name files after text the person typed: a prefix, a
suffix, a name pattern, the name of an artboard, asset, layer, slice,
data set or library, or the document's title. The prefix, the suffix,
name patterns and Batch's data set names had only /, \ and : replaced,
so on Windows a name holding <, >, ", |, ?, * or a control character
failed to save, and Save for Web without a path named its files after
the document's title as it was. A row folder name could end in a
period, which Windows does not allow, and names such as CON, NUL, COM1
or LPT1, which Windows reserves for devices, were written as they
were.
file_part, which Export for Screens' prefix and suffix and Batch's
data set names use, now writes all of those characters as -, and name
patterns use it in place of their own copy. A new safe_file_name runs
a whole file or folder name through file_part, removes trailing
periods and spaces, and adds - after a device name that is the whole
name or comes before its first period (CON.png becomes CON-.png).
Export for Screens uses it for each file, folder and zip it names, and
keeps item names distinct after it. Save for Web uses it for slice
images and, without a path, for files named after the title. Library
files use it too. This happens on every platform. Other names are
written as before, except where a changed name now equals another
item's name, which then gets a number added.
Docs: docs/mcp.md, docs/target-app-parity.md.
With Override Save or Override Export on, action.batch named each
step's file in the destination folder with the step's format exactly as
written. The save and export commands read the format in any case and
with a leading dot, so a step with "format": ".svg" wrote
"Untitled_a..svg", "SVG" wrote ".SVG", "png8" wrote ".png8" and
"template" wrote ".template".
The file now ends in the format's extension: the extension as written,
in lower case and without the dot, when it is one of the format's
(".JPEG" gives ".jpeg"), and otherwise the format's first extension, the
one Export and Save use for a file name ("png8" gives ".png", "template"
gives ".vctemplate"). An unknown format ("xyz") keeps its spelling, and
the step fails with the command's error as before.
Docs: docs/mcp.md, docs/target-app-parity.md.
File > Place could not place a Word document: a .docx file was "not a
format VectorCraft reads". The reference app's File > Place reads
Microsoft Word files as text.
File > Place now reads .docx files, written from ECMA-376 Part 1
(WordprocessingML, Transitional and Strict), and sets their text as
area type the way a text or RTF file is placed. The package is read
through its relationships. The formatting comes from the document
defaults, the paragraph style (else the default paragraph style), the
character style (else the default character style), the styles each
is based on, and direct formatting. Bold, italic, caps, small caps,
strikethrough and hidden text are toggle properties: a style that
turns one on reverses the state the styles before it give.
Each run keeps its font (theme fonts from the theme part), size, bold
and italic, underline, strikethrough, all caps, small caps,
superscript or subscript (in Document Setup's proportions), raised or
lowered text and color. Each paragraph keeps its alignment, start,
end, first-line and hanging indents, space before and after, and
direction; the start indent of a right-to-left paragraph is its right
indent. Table cells are separated by tabs and each table row is a
paragraph. Text in insertions, hyperlinks, content controls and
fields' results is read. Hidden and deleted text, field instructions,
pictures, text boxes, headers, footers, notes and list numbers are
left out.
A ZIP archive named .docx, or one that holds word/document.xml, is
read as a Word document. fileio/zip.rs gains a reader for stored and
deflated files that checks each file's CRC. A package with more than
10,000 files, a part that unpacks to more than 16 MiB or nests
elements more than 256 deep, and a document with more than 4 MiB of
text or 100,000 paragraphs or runs are refused with an error. The
Place dialog lists Word files, and file.place, file.place.info and
file.place.queue report the format `docx`. .doc files are still not
read.
The Word document tests in tests_textimport.rs fail without the
change, and text_import_fuzz.rs places random and damaged packages.
Docs: docs/file-format-parity.md, docs/mcp.md,
docs/target-app-parity.md.
1-bit TIFFs compressed with CCITT Group 3 did not open, place or
relink: the image decoder reads Group 4 only and refuses Compression 2
(modified Huffman) and Compression 3 (ITU-T T.4).
They are now decoded with hayro-ccitt, the CCITT decoder that EPS
import uses. Compression 2 rows start at a byte and have no EOL codes.
Compression 3 rows follow an EOL code and are one-dimensional, or, with
T4Options bit 0, one- or two-dimensional as each row's tag bit says.
Fill bits, FillOrder 2, either PhotometricInterpretation, strips and
tiles are read.
A file whose pixels would take more than the decoding allowance as
RGBA is refused as too large to draw. A strip or tile whose data ends
before its last row is refused as damaged, and so is a tile larger
than the allowance. The strips and tiles together are read from at
most as many bytes as the file holds.
Part of #1466.
Docs: docs/file-format-parity.md, docs/target-app-parity.md.
Optical Margin Alignment was stored and set per text object, so the
frames of one thread could differ. Turning it on in the first frame of
a thread left the lines in the other frames flush with their margins.
Type › Optical Margin Alignment and type.opticalMarginAlignment now set
every frame of a selected frame's thread, in one undo step, and the
reply's ids list them all. After any edit to a thread, its frames take
the setting of the frame that the edit turned on or off, else the
first frame's. Threading frames gives the thread its first frame's
setting, and frames taken out of a thread keep it. A file whose frames
of one thread differ opens with the first frame's setting in all of
them. This holds for native files and for documents restored from the
editing copy that an EPS, SVG, PDF or .ai file carries.
Threading text from an out port into existing area type
(text.thread.add with to) now flows the story into that frame at once.
Before, the story reached it only at the next edit of one of the
thread's frames.
Docs: docs/mcp.md, docs/target-app-parity.md, docs/type-parity.md.
Selecting other art drops Puppet Warp pins, ending Edit Text clears the
flat view, and a font scan lays type out again. None of these makes an
undo step or counts as an edit to save, but each one replaced the
document. A background save that finished afterwards compared the
document with the one it wrote by pointer, so the document stayed
marked modified. Data Recovery also wrote a new copy of a document whose
pins had been dropped.
DocState now records the documents that these changes replaced, and
DocState::same_content counts them as having the same content. The
modified flag, the end of a background save and Data Recovery use it.
After Edit Text ends, an undo state that was the saved one stays the
saved one.
Print and Print Preview name the area type and type on a path that
prints without the text past the end of its frame (#2308). The exports
that draw type gave no such warning: a PDF, EPS, SVG, EMF, WMF or
raster export of a frame too small for its text answered with no
warning about it.
These exports now add the same warning, with "isn't drawn in the
export" in place of "doesn't print". Each export counts the visible
objects that it draws. Those are the objects on the pages or artboards
it writes, or all of them in an EPS of the visible art and in an SVG
without chosen artboards. A PDF leaves out layers that don't print
unless includeNonPrinting or createLayers is set. The objects are found
with the walk that fonts::printed_type runs for Print, and typecmd.rs
builds the message with the code that builds Print's. A .ai file keeps
the whole text in its editing data and gives no warning.
Docs: docs/mcp.md (Fitting area type), docs/target-app-parity.md.
With View > Guides > Lock Guides on, Session::select drops the paths
made into guides from the selection (#1534), but commands that set the
selection another way skipped that filter and left a locked guide
selected, so the next move moved it. object.exitIsolation selected an
isolated guide path. Undo, Redo, a canceled undo group, a canceled
drag or dialog preview and a failed command.batch brought back locked
guide paths and ruler guides in the selection. Unlock All, Show All
and Paste selected the guide paths they unlocked, showed or pasted.
Locking the guides deselected them in the active document only.
The filter is now deselect_guides, which also clears the selected
ruler guides. Session::select and Session::edit run it while Lock
Guides is on, and so do object.exitIsolation, Undo, Redo,
Session::cancel_interaction, a canceled undo group and a failed batch.
Locking the guides deselects them in every open document. With Lock
Guides off, the selection is set as before.
Docs: docs/mcp.md.
File › Open, File › Place and the Links panel note an image that needs
more than the decode allowance (2 GiB, 512 MiB in the web build). The
note counted the bytes the decoder gives, one a pixel for an 8-bit or
1-bit gray image, so a 1-bit gray PNG of 23,171 × 23,170 pixels (537 MB
as gray, 2,147,488,280 bytes as RGBA) got no note, although sampling,
recoloring and Load Texture refuse it for its RGBA size.
The note now counts width × height × 4, or the decoder's bytes when
they are more, against the allowance, with the size computed in 64
bits. Such an image gets the note an oversized RGBA image gets. Images
within the allowance get no note, as before.
Object > Rasterize, Crop Image, Flatten Transparency, Expand
Appearance of raster effects and Vector Halftone draw the art with the
renderer and replace it with the result. When the art held an image
that could not be decoded, the renderer drew nothing for it, and the
result lacked the image with no message. A raster export of the same
art warns that "an image couldn't be decoded and is missing from the
picture".
These five commands now compare decode_failures before and after they
draw, as raster exports do, and their replies carry that text as
`warning`. They still make their result in one undo step. The app
shows a command's `warning` in the status bar: after a menu command,
after OK in a dialog with a preview, and after a tool runs a command
(Crop Image's Apply).
Docs: docs/mcp.md (Flatten Transparency, Expand Appearance, Document
Raster Effects Settings, Vector halftone).
A PDF-compatible .ai read through its editing data kept the PDF part's
warnings that text was kept as outlines ("text was converted to
outlines", the U+FFFD warning and the warning on glyphs that differ
from the installed font). The layers take from the page only text
nodes and the stroked paths around them, so filled outlined glyphs
don't reach the document, and type made from the text document holds
every character.
- The warnings are dropped when the document holds no glyphs kept as
outlines. Stroked glyphs that come in with stroked type keep them.
- A text object that takes the page's type, next to glyphs the page has
only as outlines, lacks those characters: a new warning counts such
objects.
- vectorcraft_pdf exports the three warnings, is_outlines_note,
is_glyph_outlines and holds_outlines.
Docs: docs/target-app-parity.md.
File > Place read text files only as plain `.txt`, and an RTF file
could not be placed. The reference app lists RTF among the formats it
places as text, keeping the file's font and style settings.
File > Place now reads RTF files, written from the Rich Text Format
Specification 1.9.1, and sets their text as area type the way a text
file is placed. Each run keeps its font from the font table, its size,
bold and italic, underline, strikethrough, all caps, small caps,
superscript or subscript (in Document Setup's proportions), baseline
shift and its color from the color table. Each paragraph keeps its
alignment, left, right and first-line indents, space before and after,
and direction. Table cells are separated by tabs and each table row is
a paragraph. Unicode characters, Windows-1252 and Mac Roman bytes and
the special-character control words are read, and of a \upr group's
ANSI and Unicode text, the Unicode text is read. Hidden text, pictures,
objects, headers, footers, notes, field instructions and other
destinations are left out, and unknown control words are skipped.
A file is read as RTF when it starts with {\rtf, whatever its name,
except a .txt file, which stays plain text. RTF files share the 4 MiB
limit of text files. Groups nested more than 512 deep share the
formatting of the deepest one read, the font and color tables keep at
most 4,096 entries, and a file with more than 100,000 paragraphs or
runs is refused. The Place dialog lists RTF files, and file.place,
file.place.info and file.place.queue report the format `rtf`.
The tests in tests_textimport.rs fail without the change, and
text_import_fuzz.rs places random RTF token streams.
Docs: docs/file-format-parity.md, docs/mcp.md,
docs/target-app-parity.md.
The EPS reader read PostScript by any name, but the Open and Place
dialogs hid .ps and .epsf files, and a .ps or .epsf file whose header
is only %! could not be opened or placed ("not a format VectorCraft
reads"). The reference app lists .eps, .epsf and .ps under Open and
Place.
- A read-only PostScript format (ps) joins the format table. A .ps
file opens and places through the EPS reader and reports the
ps format. document.export does not write .ps; file.print does.
- .epsf is an EPS extension.
- OPEN_EXTS and PLACE_EXTS list epsf and ps, so the dialogs, the CLI
help and the MCP open_file description list them.
UNASSOCIATED_EXTS lists them too: the macOS bundle declares neither.
- A .ps or .epsf file whose header is only %! now pastes as EPS when
copied in a file manager, as one with a %!PS header already did, and
clipboard.importEps reads PostScript whose header is only %!, which
it rejected before.
Docs: docs/mcp.md, docs/file-format-parity.md (.ps row),
docs/target-app-parity.md.
Native files remember the grid like the saved view, so reopening restores it.
Toggling it is still view state: no undo step, document not marked modified.
Fixes#1968
file.saveAsTemplate kept a name whose extension named another format
and wrote the template into it: {path: "card.png"} wrote .vctemplate
JSON to card.png, and card.pdf typed in the desktop save panel got it
too. file.saveAs {format: "ait"} answered that document.export writes
other formats, but document.export does not write .ait either.
file.saveAsTemplate now replaces another format's extension (card.png
is saved as card.vctemplate, or card.ait for format: "ait") and adds
the extension to a name whose extension names no format. The desktop
panel adds the template's extension to any name that does not end in
a template extension (card.pdf.vctemplate), as Save As adds its own.
Save and Save As still refuse ait, and the error now names
file.saveAsTemplate.
Docs: docs/mcp.md, docs/target-app-parity.md.