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 editing copy of a PDF-compatible .ai wrote every image as its outline
filled with its average color. Embedded images are now written with their
pixels, and linked images as placed files with a preview:
- Embedded images are rasters in the form of the version 7 specification
(section 8, https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf):
%AI5_BeginRaster, the color space, the image matrix and the XI operands
of current files, then the samples in binary in a %%BeginData section.
Gray, RGB and CMYK images keep their samples (CMYK images their ink
amounts), and see-through images their alpha channel after the color
samples. The matrix is written to ten decimals.
- Linked images are placed files (section 16): the matrix, the box of the
file's pixels and its path, %%IncludeFile, the folder relative to the
document's (when the relative path ends in the path's file name), and a
preview of at most 256 pixels a side.
- Images that can't be decoded or have more than 33,554,432 pixels (the
most the reader reads) are written as their outlines filled mid gray,
images of 16 bits a channel (CMYK TIFFs included) at 8 bits, and linked
images in opacity masks and pattern tiles as embedded images, each with
a warning.
Each of these is written in the form VectorCraft's .ai reader reads, and
the tests read the written data back through that reader.
The reader now gives an image its name and opacity mask when the
operators that end its raster (XH, Xr and the N after %AI5_EndRaster) come
before its dictionary, reads an empty relative folder as the document's
own folder, and looks for the %%EndData that ends an image's samples
after the alpha channel its XI operands give it, preferring one whose next
line is XH, so samples that hold the marker's bytes are read whole.
Tests: the round trip of every kind of image and its state, a second write
of what is read back giving the same bytes, the raster and placed-file
forms, previews of large linked images, and the engine's editing copy with
every save option; a fuzz case for damaged images and placed files.
Docs: docs/file-format-parity.md.
The editing copy for other apps now writes linear and radial gradients and
grid patterns as themselves instead of as average colors and cut tiles.
- Gradients the art uses are defined in the setup (`n Bn`, then
`%AI5_BeginGradient` … `Bd`, the stops as `Bs` with their opacity and
midpoints, `BD`), and gradient swatches nothing uses in a non-printing
block. Stops keep gray, CMYK, RGB and spot colors at a tint. A
gradient that isn't a swatch gets a name that no swatch or pattern of
the document has.
- Each fill or stroke with a gradient is an instance around its painting
operator (`Bb`, `Bh`, `Bg`, `Xm`, `Bm`, `Bc`, `BB`), on a compound
path's last subpath and on clipping paths too. `Bg` places the gradient
relative to the path: a linear one by its angle and its start and length
as fractions of the path's extent along it, a radial one by the matrix
that takes the unit circle onto its extent ellipse, its center as
fractions of the path's bounds in that circle's space and its radius as
a multiple of the one the center's distances to the right side and to
the bottom give. In a pattern's tile the placement is mirrored top to
bottom.
- Patterns whose tiles make a grid and hold their art are defined with
`%AI3_BeginPattern` … `E`, the tile's box first, and fills and strokes
paint with `p` and `P`; gradient and pattern swatches are in the palette.
- Freeform gradients, gradients along or across a stroke and gradients
without stops, with more than 256 stops or without extent are written
as their average color, and other patterns as their tiles cut to the
shape, each with a warning. Gradients that dither or mix perceptually
are written as classic ones, stops linked to global process colors as
their colors, and pattern swatches without the placement that moves,
turns or scales their pattern, with warnings.
- Reading: an unpainted box that a pattern's definition marks as its
tile's box (`AIPattern_Editor_Backing_Tile_Rect`) is no longer read as
art, and a linear gradient whose stops all lie at one place keeps each
stop on its side.
The definitions, instances and patterns follow sections 3.4, 3.5 and 14
of the version 7 specification
(http://mercator.elte.hu/~saman/hu/okt/AI7FileFormat.pdf). The stop
opacity, `Bg`'s placement and the marked tile box follow the newer form
that current .ai files carry, which that specification predates.
Each of these is written in the form VectorCraft's .ai reader reads, and
the tests read the written data back through that reader.
Docs: docs/file-format-parity.md.
The editing copy of the art now writes a guide as its path followed by
`(N) *`, or `(n) *` when the path is closed, without the `(N) *` before
the path. The legacy version 7 .ai keeps both, as section 6 of the
version 7 specification shows them
(http://mercator.elte.hu/~saman/hu/okt/AI7FileFormat.pdf).
The editing copy of the art that a PDF-compatible .ai carries now holds:
- Transparency: `mode opacity isolate knockout shape Xy` (the blending
mode's number, Knockout Group 0 off, 1 on, 2 neutral), written when it
changes: in a path's paint after its colors, before a guide, and after
a group's end followed by `0 0 Xd` and `6 () XW`. A group without
transparency of its own ends `9 () XW`, and a group's name comes
before its state block. A layer's transparency follows its art: `Xy`,
`0 0 Xd`, `7 () XW`, `LB`.
- Clipping groups as `q`, the art, the clipping path or compound path last
with `h` (or `H`) and `W` before its painting operator, `Q`, as section
5.6.2 of the version 7 specification shows them
(http://mercator.elte.hu/~saman/hu/okt/AI7FileFormat.pdf). A clipping
layer whose clipping object is a path or compound path sets the last
`Lb` flag and writes its clipping path last.
- Opacity masks as an `AI9_Mask` entry in the object's dictionary: the
mask's art between `X=` and `X+`, and Clipping, Inverted, Disabled and
Linked.
Before, clipping groups were written as their art cut to the clipping
path, and transparency and opacity masks were left out. Still left out,
with a warning: the opacity and blending mode of single fills and
strokes, opacity masks on layers (disabled ones too), and opacity masks
nested more than 8 deep. A clipping group or layer whose clipping object
isn't a path or compound path is written as a group of the art cut to
that object, in a layer that doesn't clip, and so are the clipping
groups inside that art. A clipping path's paint that a plain fill and
line can't draw is written as paths inside the clipping, fills under the
art and strokes over it, each side in a group of its own when it has
several, with the clipping path's transparency, lock and hidden state;
the clipping path's opacity mask stays on the clipping path, with a
warning. A clipping group without members is an empty group, and an
empty clipping layer is an empty layer.
Each of these is written in the form VectorCraft's .ai reader reads, and
the tests read the written data back through that reader.
Reading .ai editing data: an `Xy` after a layer's last group or compound
path, followed by the layer's `7 () XW`, is the layer's (a following
`6 () XW` keeps it the group's); groups, compound paths and layers keep
Opacity & Mask Define Knockout Shape.
The `.ai` option `editingData` and docs/file-format-parity.md describe
what the copy now holds.
Each artboard's DisplayMark was written as 0. It now holds the center
mark (1), cross hairs (2) and video safe areas (4), as the reader reads
them.
A pattern fill that would place more tiles than a fill places came back
with no tiles and was written as an empty group, so the shape was
missing from the editing copy. Such a shape is now filled gray, as a
pattern that isn't found is, with a warning.
Artboards that together did not fit on the 16,383 pt canvas were
written with %AI24_LargeCanvasScale: 1, so part of them lay outside
the canvas. The editing data now declares a scale of 10 for them and
writes the art, boxes, artboards, strokes and effects at 1/10 of their
size, symbol and pattern art included.
Reading such a .ai, the editing data comes in at its declared scale
when it matches the page that way rather than in the units of the
page's UserUnit, also when the PDF part can't be read, and a file saved
without its PDF part applies the declared scale too.
The comments on the %%Creator line and on the artboards' Recorded and
NotRecorded parts of the document dictionary now describe what the
writer writes.
The editing data put ArtboardArray and AIDocumentCanvasSize in the part
of the /Document dictionary marked /NotRecorded. Apps that read the editing
data do not read artboards from there: a file opened with one default
artboard the size of the first, the other artboards and the names were
lost, and the art landed one artboard's height above it. The two entries
now close a /Recorded part, followed by an empty /NotRecorded one, as in
the files users sent.
Instances of symbols that hold instances of other symbols multiply, and the
editing data wrote every level for each instance above it, limited only by
nesting depth: eight levels of 100 instances each could hang a save or run
out of memory. The writer now counts the objects of symbol art it writes,
symbol instances and inline graphics alike, and leaves out instances past
16,000,000 objects in all, as the PDF part does, with a warning.
Type is written as its outlines, and the outlines leave out inline
graphics, so the editing data lost them while the PDF page drew them. Each
inline graphic is now written as its symbol's art where the layout places
it, over the characters and under the object's own fills and strokes above
the Characters row, in the order the PDF page draws them.
A pattern fill is written as its tiles cut to the shape. A group in the
tile art with fills or strokes of its own was written as its members alone,
without that paint, because only the layers and the symbols' art were baked
before writing. The patterns' tile art is now baked the same way as the
symbols' art, so its group paint and geometry effects are written as plain
paths.
The editing data wrote art space with its origin at the first artboard's
bottom-left corner, its artboard records lacked keys a reader requires, and
its %%Creator line made the reader measure %AI3_TemplateBox on an
8640-point canvas. Other apps opened the art 3,871.5 points away from its
artboard.
Art space is now ruler space, as in the .ai files users sent: y up from the
first artboard's top-left corner. The artboards together are centered on the
16383-point canvas. Each artboard's record gives its top-left corner there
(RulerOrigin), its pixel aspect ratio (PAR), DisplayMark and whether it is
selected, and %AI3_TemplateBox holds the canvas's center. %%Creator names
the format's app and holds no version number. %AI3_Cropmarks and
%AI3_TileBox give the first artboard, %AI5_ArtSize is 14400 14400, and the
/Document dictionary gives the canvas size.
A file with several artboards carried the data on its first page only, and
other apps opened it as a PDF, page by page. Every page now refers to the
one data dictionary, as the files users sent do.
The format details come from users' own .ai files, static analysis of
Illustrator's program and the user's manual testing in Illustrator.
The editing data now has the layout of the .ai files it was learned from:
- Lines end in CR LF. Line breaks inside strings stay escaped.
- The header adds %%For (the document's author), %%Canvassize 16383,
%AI7_ImageSettings, %AI5_ArtFlags, %AI5_TargetResolution,
%AI5_OpenViewLayers (a digit for each layer, 7 shown and 6 hidden) and
%AI7_GridSettings (the document's grid spacing and subdivisions), in the
order those files have them.
- The setup starts with 0 Bn (no gradient definitions).
- The palette starts with the Registration swatch: Xs in a CMYK document,
Xz with black's CMYK equivalent in an RGB one. Reading the file back does
not add a swatch for it.
- Each layer and sublayer carries its name as an XML id in an /ArtDictionary
after Ln. An object named like a layer gets a numbered id.
- The LastModified dates of the PieceInfo dictionaries end in +00'00'.
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.