Commit Graph
23 Commits
Author SHA1 Message Date
dotCooCoo d3a323284c Merge main into fix/ai-os23-2831 2026-11-08 18:52:59 -08:00
dotCooCoo c94269ff72 Type: Composite Fonts… sets each class of character in a font of its own
Squashed from #6244 onto main.
2026-11-09 03:08:23 +07:00
dotCooCoo b8233f7c19 .ai save: a reopened file opens on the artboard active when it was saved
Squashed from #5975 onto main.
2026-11-08 11:22:28 +07:00
dotCooCoo d58d47312f Legacy .ai export: black-and-white colorized images save as image masks
A colorized image whose pixels are all black or white (or clear) is
written as a 1-bit image mask (ImageMask 1): its color as the fill, a 0
bit for each black pixel and a 1 bit for each white or clear one. The
legacy reader opens it colorized again with the same clear pixels, so
the engine no longer flattens or omits it. Other colorized images are
written with their color in their pixels, over white, with the note
for transparent images.
2026-11-06 15:33:35 +07:00
dotCooCoo 9275b902a9 PDF/.ai import: a file whose type all comes from its text document opens without its PDF part
A PDF-compatible .ai (or a PDF saved with the editing data) now opens
from its layers without its PDF part being imported, and without being
compared with its page, when:

- its text document makes the type of every text object, hidden ones
  included, at least one text object shows, and none is drawn by the
  appearance of the layer or group it is in;
- its layers hold no unread object, no placed file with a second
  placement and no live envelope with drawn art to fall back on, and
  no image too large to read;
- it declares no large canvas, and its artboards are its pages' trim
  boxes one for one, in points or in the first page's UserUnit.

A file whose layers draw differently from its page then opens from its
layers instead of its PDF part. The bleed comes from the pages' bleed
boxes. The check runs only when no pages are picked, layers and the
editing data are on, the crop box is asked for and type comes in as
text. Files saved without PDF compatibility, files whose PDF part
draws only type or nothing, and files whose PDF part's content can't
be read take the same path when they qualify: their bleed comes from
the pages' bleed boxes, pages in units larger than a point draw them
at their size in points, and the note that the PDF part couldn't be
read is not given, since its content is not read.

Files that don't qualify open as before, and the editing data decoded
and read for the check is reused after the PDF part is imported
(layered_ai_from).

When a file whose PDF part can't be read is scaled up to its size in
points, its text wraps are now laid out again at that size, and the
default graphic styles of a file without graphic styles of its own are
no longer scaled. The same applies on the new path.
2026-11-04 19:41:56 +07:00
dotCooCoo f8095edf0d Merge commit 'refs/check/4968-main' into feat/ai-editing-data
# Conflicts:
#	crates/eps/src/lib.rs
#	crates/pdf/src/lib.rs
#	docs/file-format-parity.md
2026-11-02 18:47:39 -08:00
dotCooCoo 100730d4d3 .ai import: a file whose PDF part can't be read opens from its editing data
Squashed from #5051 onto main.
2026-11-02 12:04:04 +07:00
dotCooCoo 9726e691f5 .ai import: names that aren't UTF-8 can be read in a legacy code page
A legacy .ai stores layer, object, color, gradient, pattern, style and
swatch names as bytes, without saying which code page they are in.
Names that aren't UTF-8 are still read as Windows-1252 by default, and
now each gives a warning naming it and the encoding it was read in (the
first three, then a count).

document.open takes legacyNameEncoding (windows-1252, macintosh,
windows-949 or euc-kr, shift_jis, gbk, big5) and vectorcraft-cli
convert takes --legacy-name-encoding, to read those names in another
code page. Definitions and the fills that name them are read alike, so
they still match. Type, comments, dictionary keys and PDF layer names
are read as before.

encoding_rs becomes a direct dependency of vectorcraft-eps.

Part of #2831
2026-11-01 12:17:42 -08:00
dotCooCoo e7cb16b66d Merge main into feat/ai-editing-data 2026-10-31 20:20:39 -07:00
dotCooCoo 1840b9a582 EPS export: gradients of spot colors keep their spot inks
EPS export wrote every gradient with a spot color stop in process
colors. A gradient of tints of one spot color and paper white is now
written in that ink's Separation space, at Level 2 and 3. At Level 3, a
gradient that mixes a spot color with CMYK or gray colors, or two spot
colors, is written in a DeviceN space of the process inks its stops use
and the spot inks, each spot ink with the CMYK equivalent its Separation
space has. An off-center midpoint is a stop with the inks halfway
between its two stops. Paper white is an exact white, whether or not
the stop is linked to a process swatch.

These gradients are still written in process colors, each with its own
warning: a mix with other inks at Level 2; a mix with RGB or Lab colors
in an RGB export (a CMYK export converts those stops to CMYK first); a
DeviceN space that would need more than 32 colorants or a spot ink named
Cyan, Magenta, Yellow, Black, All or None; and a Perceptual gradient.
The old warning for every spot gradient has been removed.

The header lists two or more spot colors with each %%+ line right after
the comment it continues: %%DocumentCustomColors and %%CMYKCustomColor
were interleaved, so a DSC reader took the second ink's name as part of
the first ink's %%CMYKCustomColor.
2026-11-01 06:11:12 +07:00
dotCooCoo f33617142c Save As .ai: carry editing data that other apps open as layers and paths
A PDF-compatible .ai now also carries, on its first page's PieceInfo, an
editing copy of the art in the form VectorCraft's own .ai reader reads:
AIPrivateData streams of 64 KiB holding the program compressed with
Zstandard after %AI24_ZStandard_Data, an AIMetaData stream of its header
comments, and the ContainerVersion, CreatorVersion, NumBlock,
RoundtripStreamType and RoundtripVersion entries.

The program holds the header comments, the color swatches and color
groups, every artboard, layers and sublayers (name, order, visibility,
lock, color, printing, dimming), groups, paths, fills and strokes (width,
caps, joins, miter limit, dashes, overprint, fill rule), process, spot and
global colors, guides, hidden and locked objects and object names. Type,
gradients, patterns, images, symbols, live objects, brushes, clipping
groups and compound paths are written as plain filled and stroked paths,
each with a warning; transparency and effects other than geometry effects
are left out.

The .ai option editingData (on by default, Include Editing Data for Other
Apps in Save As) turns it off. The legacy version 7 writer is unchanged;
the path, guide and line style operators both writers write move to
eps/src/ai_ops.rs. LoadOptions::native off opens a file from its editing
copy or pages instead of the VectorCraft document it carries.
2026-10-28 16:55:41 -07:00
Grant Colloty 84d5d8797f EPS/.ai editing data: type keeps stacked art styles as live appearance
**What:** Illustrator type styled through the Appearance panel came in as its drawn look (outlined compound paths). Comic and "text effect" templates are the common case, and the type couldn't be retyped. This keeps it as editable type with the style as a live VectorCraft appearance.

**Changes**
- **Art styles parsed as a tree.** Compound filters close at their `;`, basic ones at `/Part`. Each fill and stroke in a stack keeps its own effects, opacity and blend mode, and object-level effects stay on the object.
- **Type keeps any plain stack.** That covers several fills and strokes, pattern and gradient fills, Warp, Offset Path, Transform with copies, Drop Shadow, Outer Glow, Feather, Gaussian Blur and Bas Relief. Bas Relief settings are left at their defaults, because Illustrator stores them in a Photoshop plug-in's binary block. Gradients and patterns are placed as the drawn look places them. Other objects keep the existing plain-style rule, since their drawn look is the more faithful option.
- **Point type without a frame matrix.** Illustrator 24 omits the matrix when point type is neither rotated nor scaled. Such stories were left out entirely; they're now read with an identity matrix.
- **Page comparison.** The page's rendering of a style isn't held to match VectorCraft's live rendering where the replaced drawn look lay, and styled made type isn't pruned as unseen.
- **EPS override.** `editingData: "always"` / `convert --editing-data` now applies to Illustrator EPS as well as `.ai`.

**Tests:** 6 new tests, written from scratch; `vectorcraft-eps` passes all 298 on current main. clippy `-D warnings` and rustfmt are clean for eps, engine and cli. `docs/file-format-parity.md` has been updated, including its revision history.

**Clean-room note:** I found the bugs with a user's licensed stock EPS, used locally only; nothing from it is committed. Filter and key meanings come from the public Illustrator Effect docs and public LiveEffect scripting examples (`jntp`, the Warp style order).

**Not included:** VectorCraft's pixel effects treat transparent content as empty instead of using Document Raster Effects Settings › Background (white by default). Because of that, Bas Relief on a transparent pattern gives flat grey. That needs a separate renderer change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-10-21 23:16:30 +07:00
dotCooCoo 819efd465f EPS import: a PostScript error shows what its name means in the interface language
An EPS or PostScript file that stops on a PostScript error named the
error, such as typecheck or rangecheck, with the operator and the
procedure, but did not say what the name means in any language.

The warning or error now gives the name as PostScript spells it,
followed by VectorCraft's own wording of what it means in the reader:
"a PostScript error (typecheck in `add`; a value has the wrong type for
the operator)". vectorcraft_eps::POSTSCRIPT_ERRORS lists the 14 names
the reader raises other than undefined, with their meanings, and a
test checks that each of those names has one. Each meaning and the
new message shapes have a row in every catalog. i18n::message now
translates placeholder values five levels deep instead of three, which
the status bar's line for an EPS file that can't be opened needs.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-21 16:46:17 +07:00
dotCooCoo 05264b2801 MCP: document.pdfInfo answers one page for an EPS, a .ps file or a PostScript .ai
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.
2026-10-19 00:03:45 +07:00
Dan Green 6ce1a622be Part of #868: legacy .ai type as text objects and hidden objects kept; legacy type read
Type is written as version 7 text objects (§15): point type, area type and type on a path, with
fonts by PostScript name re-encoded to Windows-1252, and each run's paint, size, scaling,
tracking, leading, alignment and indents; type 8-bit text can't hold (or all, with outlineText)
is outlines. The format hides only layers, so hidden objects go to a hidden layer above their
own (hiddenObjects "layer", the default), are left out ("omit") or show ("visible"). The .ai
reader reads legacy type (To … TO, TE, %AI3_BeginEncoding) and ignores Ar; the import fuzzer
gets legacy type tokens.
2026-10-16 01:36:24 +07:00
Dan Green eb320593dc Part of #868: legacy .ai writer to the published version 7 specification
export_legacy_ai writes a document as a PostScript-form .ai of version 7 (one artboard per
file), keeping every layer (name, order, Hidden, Locked) and its groups, compound paths and
clip groups. Closed paths end on their start point, so they keep their corner. The .ai reader
learns what the writer emits, and the tests read every file back.
2026-10-16 01:36:24 +07:00
bflatasticandClaude Opus 5.5 c599522061 Fix #735: Illustrator EPS and .ai files match their page in Fit to Artwork Bounds and the page check
- Fit to Artwork Bounds fits the art exports draw: hidden objects and
  layers, guides and template layers don't count, and the bounds stop at
  a layer's clip.
- The comparison with the page catches an object one draws that the
  other doesn't (pixels whose neighbours all differ, away from type), a
  light colour against white, and, for an EPS whose page is its art's
  box, art the layers print outside it.
- textAs: outlines opens a .ai whose type shows from its PDF part, which
  has the outlines.
- document.open takes editingData: false to open an Illustrator EPS or
  .ai as its page or PDF part only, for print pipelines.

Part of #637.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:51:50 +07:00
bflatasticandClaude Opus 5.5 4b9f9b9bc2 Part of #637: .ai files saved without PDF compatibility open from their editing data
Such a file's PDF part is a placeholder page, which was refused. When
the file carries editing data, it now opens from that alone: its
structure as a PDF-compatible file's, its point type made from the text
document, and type it can't make (area type, type on a path) left out
with a note that marks the import as lossy. Without editing data, or
with pages picked or layers off, the placeholder is refused as before;
damaged editing data says so.

The page-order note is no longer given when the page has no type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:35:22 +07:00
Matthew FarrowandClaude Sonnet 5.5 ec43ac5a1e Read Illustrator EPS and .ai layers from their editing data
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>
2026-10-09 03:49:49 -04:00
bflatasticandClaude Opus 5.5 9f3b992ecc EPS import: other apps' EPS open as vectors, a preview that stands in says why
Discord feedback: "EPS files are always rasterized: they open as bitmaps, in
other programs as vectors". Every EPS saved by Illustrator 2026 fell back to
its TIFF preview (`gcheck` was unknown), and cairo/Inkscape files stopped at
their first image (`status`), dropped masked images (ImageType 3) and failed
on mesh gradients; the warning didn't say where the program failed.

Fallback never silent:
- The warning names the PostScript error, the operator that raised it and
  the named procedures it ran in, innermost first ("a PostScript error
  (typecheck in `add`), in `inner` in `outer`"); errors caught by `stopped`
  aren't reported. It reaches the status bar, document.open, app.open and
  file.open (which now return document.open's result, so MCP's open_file over
  the control channel gets it), and `vectorcraft-cli info` ("warnings").
- A file whose PostScript can't be read opens as its TIFF preview, else its
  Windows metafile preview (as vectors, scaled to the bounding box), else its
  EPSI bitmap. es @msg rows for the new messages.

Interpreter:
- Operators programs probe or use: gcheck/scheck, rootfont,
  currentcolorrendering, currenthsbcolor, status, token, read,
  bytesavailable, fileposition/setfileposition, resetfile, write*, pdfmark
  (marks dropped, as Ghostscript does), execform, nulldevice, strokepath,
  pathforall, user paths (ufill/ueofill/ustroke/uappend/upath/setbbox, the
  encoded form too), setcachedevice2, findcolorrendering, cache, device and
  VM queries, revision; (%stdout) (w) file gives a sink.
- `//name` is replaced by its value when read, inside procedures too.
- Tiling patterns become pattern swatches (their PaintProc drawn in pattern
  space, one cell; uncoloured ones in the colour under them; makepattern
  copies share one swatch); nested patterns stop at 8 levels.
- Type 3 fonts draw their glyphs (BuildGlyph/BuildChar, widths from
  setcachedevice/setcharwidth) as outlines grouped per string, spaced by
  ashow/widthshow/awidthshow/xshow/yshow/xyshow/kshow; glyphshow and
  stringwidth/cshow use them.
- Shadings of types 1 and 4-7 become gradient meshes (data from arrays,
  strings or files, packed or not, flags sharing edges), as fills through a
  shading pattern too; sampled functions (type 0) are evaluated;
  GradientMesh::coons/triangle are shared with PDF import.
- ImageType 3 masked images, interleaved by sample, by row or in separate
  sources; ReusableStreamDecode reads its data when made; Flate/LZW PNG and
  TIFF predictors.
- Bounded: DeviceN at 32 inks, sampled functions at 2^24 samples and 32
  outputs, 4096 patches a shading, strokepath ignores dashes finer than 1e5
  pieces, Type 3 glyphs nest 8 deep (a glyph that shows itself overflowed the
  stack before the limit), 64-bit index maths for 32-bit targets.

Tests: a synthetic program per generator (cairo/Inkscape, its Type 3 fonts,
matplotlib, CorelDRAW, Affinity, LibreOffice, Ghostscript eps2write,
Illustrator procsets), meshes and function shadings, masked images, fault
messages, probing operators, metafile/EPSI previews, `//name`, user paths,
hostile patterns/glyphs/shadings (run against main, 12 of the 14 generator
and feature tests fail; CorelDRAW- and LibreOffice-style programs already
read there). The import fuzz vocabulary covers the new operators. Checked
against real files made here with Inkscape 1.4 (cairo) and Illustrator 2026
(its own format and the CS-to-CC compatibility formats; all fell back or
stopped early on main and open as vectors now; its Illustrator 8 format
needs the next commit); those files aren't committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:23:36 +07:00
bflatasticandClaude Opus 5.5 0428e56342 M4.92: Print to a PostScript file
file.print {format: "postscript"} (the default for a .ps path) writes the
print job as Level 3 or 2 (level) PostScript instead of a PDF: DSC comments
with %%Pages the page count, one page per sheet with its paper size,
printer's marks in Registration, separations naming their ink (%%PlateColor)
with its halftone screen, a fixed flatness and film negatives. Transparency
is flattened first with flattenerPreset. The answer says which format was
written. In the app, a PostScript job is always saved, never sent to the
printer (the Print dialog's own options are unchanged).

The PDF print's layout is exposed as vectorcraft_pdf::plan (the PDF writer
uses it too); vectorcraft_eps::print draws its sheets with the EPS writer's
document walk, each sheet once however many copies print.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:01:36 +07:00
bflatasticandClaude Opus 5.5 6752b22783 M4.91: EPS and PostScript files open and place
document.open and file.place read .eps files and PostScript by any name
(PostScript .ai opens as artwork, .ait as a new untitled document). An EPS
file VectorCraft wrote restores the document it carries (restored: true);
other files run through a small clean-room PostScript interpreter (paths,
colours and spot inks, gsave/grestore, concat, line state, bind def,
procedures, loops and dictionaries, clips, shadings, images through the
decoding filters, type as point type), and fall back to their TIFF preview
with a warning when the program can't be read. Operation, nesting, memory
and path budgets keep hostile files from hanging or exhausting memory.

The clipping-group nesting the metafile reader used moves to
vectorcraft_doc::clipnest, shared by both readers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:51:54 +07:00
bflatasticandClaude Opus 5.5 4c4850e745 M4.76: EPS export (PostScript Level 2 and 3) with EPS Options, TIFF previews and the document embedded
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>
2026-10-05 13:50:50 +07:00