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.
Tab-separated rows with the same number of cells (quoted cells too)
become a group of point type, one per cell, in columns and rows over
0.5 pt gridlines; other text pastes as before. Fixes#5296.
Each visible object with a URL (Attributes panel) is written as a Link
annotation with a URI action over its bounds on the page, trimmed to its
clipping groups. Hidden objects, non-printing layers and printed sheets get
none; PDF/X files leave them out with a warning. URIs are percent-encoded
to 7-bit ASCII.
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.
A .ai that comes in as its page or PDF part (editing data with no layers, layers that can't be read, or a page fallback) gets none of the editing data's symbols: layered_ai_from fills symbols only from build_from. The editing-data paragraph now says so.
The Known failures bullet in docs/file-format-parity.md kept stale copies
of its earlier wording after several merges: guides were still listed as
lost although they open as ruler guides since #5057, and the same clauses
about #868, #1027 and #758 repeated several times. The bullet now states
each fact once, and keeps every legacy .ai reading note it carried.
The reader measured a symbol's definition only to find the page art an
instance covered, so every file with a placed symbol instance opened
with that instance as drawn art, or as its page.
- Each `%AI14_BeginSymbol` definition is read by a reader of its own into
a symbol of the document, with its art (in the symbol's space, y down),
the registration point its dictionary gives
(`AI13PatternRegistrationType`, 1 to 9 from the top left) and the
instances of other symbols in its art. A definition whose art isn't
read whole isn't kept: type, placed files, non-native art, pattern
fills, a gradient or an image the reader can't read or place, and
anything else the reader doesn't read. Its instances come in as the
page draws them, within the box of the art that is read. A definition
that holds an instance of a symbol the file doesn't have isn't kept
either, nor is a definition holding an instance of one of those, and
their instances aren't read. A definition after the first layer isn't
read.
- `%AI24_BeginSymbolList` orders the symbols as the Symbols panel lists
them. A name read twice is numbered ("Dot 2"), and instances name the
first symbol with the file's name.
- `/SymbolInstance` with `/SymbolRef` and `/RTransformMatrix` comes in as
a live instance, with the hide and lock set before it and the opacity,
blending mode, isolation and knockout of the `Xy` after it, as a
group's. An instance with appearance changes of its own
(`AI19SymbolAppearanceMap`), of a symbol that isn't read, or placed
only by `/Matrix` or `/Transform`, comes in as the page draws it.
- Symbol art takes the gradients and art styles read before it, and an
object in it linked to a listed graphic style keeps the link.
- In a symbol's art, the matrices of instances, gradients and images are
read in the canvas's space, y up, about the canvas point of the ruler
origin. Without `%AI3_TemplateBox` an image can't be placed, so the
symbol isn't kept.
- At most 4,096 definitions are read, and a symbol list names at most
16,384 symbols.
Tests: reader tests for each case above, and fuzz cases for damaged and
hostile symbol data.
An art style with a Distort & Transform effect opened every object but
type as its drawn look. Objects other than type now open as themselves
with these effects live:
- Any object takes a Tweak that moves nothing and Pucker & Bloat at 0,
beside the effects it already took.
- A path, compound path or group takes Transform without copies, or a
Free Distort whose corners make a parallelogram, when it is the only
effect. Neither the style nor anything in the object may paint with a
gradient or pattern. Everything in the object must be a path, compound
path or group without a clip, an opacity mask or effects of its own.
When Transform scales, rotates or reflects, nothing in the object may
be stroked.
- An unstroked path or compound path whose segments are all straight
takes Twist when it is the only effect.
Other objects keep the drawn look, as do Zig Zag, Pucker & Bloat of
another amount, a Free Distort with other corners and Transform copies.
Docs: docs/file-format-parity.md, docs/target-app-parity.md.
The private editing data list in docs/file-format-parity.md now says
that several clipping paths in a clipping group, a layer or an opacity
mask's art clip to their intersection, that a compound path of clipping
paths stays one clip, and that clipping paths that would nest the art
past the document's 32 levels stop the editing data from being read, as
#5789 made the reader do.
A tab definition in type of the legacy format (`numDots numTabs Xb`, a
`leader decimal tabType tabPosition Xe` for each stop, then `XB`) was
skipped, so every paragraph opened with only the default stops every
36 pt. The stops it describes now become the paragraph's tab stops
(left, center, right or decimal, at their positions), and they last
into the next paragraphs and text objects like the other paragraph
attributes. A definition with no stops, or whose descriptions were all
skipped, leaves the default ones. A description with an unknown type or
a position outside 0 to 100000 pt is skipped, and so is one past the
first 1024 of a definition. A text object's `To` ends a definition that
an earlier object left open.
Docs: docs/file-format-parity.md.
VectorCraft now reads `CropAreaActive`, an `/Int` in the dictionary of
the editing data that holds the `ArtboardArray`, as the index of the
active artboard in that array. The reader ignored it before, and every
file opened on its first artboard.
The opened document starts with that artboard active, so the view the
document opens at and the Artboards panel use it. The index counts
every entry of the array, so an artboard that is left out (one with no
area) does not shift it. When the entry is missing, is not an `/Int`,
or names an artboard that does not open, the first artboard is active,
as before. A file that comes in as its PDF part, with its pages placed
where its artboards are, opens on the page of the active artboard, also
when the reading stops before the artboards. The same reader serves the
editing data of EPS and PDF files.
The active artboard is kept on the opened document only; native files
do not save it.
A linked image read from a version 7 `.ai` file can keep its path in
the classic Mac OS form, with colons between folders
(`Macintosh HD:Images:photo.tif`). The link's name was the whole path,
so the Links panel showed it as one long name, and Relink to Folder,
the search of the document's folder and Find Linked Files looked for a
file with that whole string as its name.
A path with a colon and no `/` or `\` is now read as a classic Mac OS
path. Its name is the part after the last colon, and `links.info` gives
the rest as `location`. Relink to Folder, the document-folder search and
`links.findFiles` look for that name, and Find Linked Files compares the
path's folders with the folders of the files it finds. The link keeps
the path as stored. Paths with a `/` or `\` keep those as separators,
so a file name may still hold a colon (`/Users/a/x:y.png`).
Docs: docs/mcp.md (Links), docs/file-format-parity.md (version 7
linked images).
In the legacy format, `Tr` sets the rendering mode of the type that
follows. Section 5.7 of the version 7 specification says that modes 4 to
7 make the text object a mask: 4, 5 and 6 also fill, stroke, or fill and
stroke it, and 7 paints nothing. Sections 15.3.2 and 15.4.5.1 add that
these modes are used in pattern definitions, and the reader does not
read type in a pattern. The reader painted 4 to 6 like 0 to 2 and never
clipped, so the art a text mask clips showed whole.
A text object with characters in modes 4 to 7 is now a clipping path of
its clipping group (`q` ... `Q`, or the layer), as a path painted with
`W` is, and the whole object is the mask. Beside other clipping paths it
nests as they do, and the mode lasts into later text objects. Area type
with linked areas (threaded frames, also when the reader's limits keep
one frame), and type in wraparound text or in a compound path, a group
inside them included, doesn't clip, with a warning.
A `Tr` operand that is not a whole number from 0 to 9 now leaves the
mode as it was; before, 4.5 read as 4 and -4 hid the type.
The new warning and the reader's warning about linked type areas left
out get rows in every catalog.
The Adobe Illustrator File Format Specification 7.0, section 4 (p. 51),
says the name shown for a global object is the part of its name in the
file before the newline
(https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf). The
legacy reader kept the whole name, so swatches and patterns came in
named "Sky\n1". The reader now applies this rule to the files of every
version it reads.
- Gradient swatches, gradient fills, pattern definitions, pattern fills,
palette cells and art-style pattern fills use the part before the
newline. Lookups inside the reader still use the whole name.
- When that name is taken, the first free "Sky 2", "Sky 3" is used.
Taken names are None and Registration, the palette's own swatch and
group names, the default swatches when no palette replaces them,
named colors (color operators, gradient stops, version 7 pattern
strings, the header's and an EPS program's custom colors), the names
of gradients and patterns that have no newline, and the names given
to earlier gradients and patterns. Only content the reader reads
counts: not the alternates of versioned content, the prolog, skipped
sections or what follows the trailer.
- A name with nothing before the newline comes in as "Gradient" or
"Pattern". Names without a newline do not change.
- docs/file-format-parity.md lists the rule.
In the legacy format, `TZ` makes a re-encoded font from an existing one
and can give it encoding pairs of its own, which change only that font's
encoding. Its `useDefault` operand says whether the file's `TE` encoding
also applies to the font (Adobe Illustrator File Format Specification
7.0, section 3.3.1,
https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf). The
reader skipped the body of `%AI3_BeginEncoding` sections and ignored
`TZ`, so every string was decoded with `TE`'s encoding.
Strings in a re-encoded font are now decoded with its own pairs, then
with `TE`'s encoding when `useDefault` is 1, and as Windows-1252 for the
codes left. Strings in other fonts keep `TE`'s encoding. The reader
reads `TZ` in the body of an `%AI3_BeginEncoding` section and wherever
else it appears in the script, with or without the direction operand
and the Multiple Master weight vector. The font names are the last two
names, and only numbers and the weight vector may follow them; the
reader does not use a `TZ` of another shape. The first `TZ` for a font
is used.
`TZ`'s font names also map the re-encoded font to the font it was made
from, as the `%AI3_BeginEncoding` comment does. Type in a re-encoded
font that no such comment names now opens in the font `TZ` names; when
both name the font, the first one read is used.
The version 7 and version 3 file format specifications list three text
operators the editing-data reader did not dispatch, so a file whose type
used any of them on a visible layer did not open from its editing data:
- `(text) justified TS` (special characters): the reader adds its
characters to the story, as it does for `Tx` and `Tj`.
- `Te` (end render) is read and ignored. The reader accepts it with any
number of operands.
- `TM` (pop text matrix) is read and ignored.
Tests cover each operator in a text object, `TS` without a string and
outside a text object, and `Te` inside a tab definition. The import fuzz
tests add the operators to the legacy type tokens.
The version 7 file format specification gives `XI` and `XF` an
ImageMask operand: 0 is an opaque image, 1 is an image that is
transparent and can be colorized (sections 8.1, 8.2 and the 1-bit
examples in 8.4,
https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf). The
reader ignored the operand, so a 1-bit image mask opened as an opaque
black-and-white image.
A 1-bit gray image with ImageMask 1 now opens with the fill color set
before it as the color that colorizes it. A spot or global color keeps
its swatch and tint. When the fill is a pattern, a gradient or none,
the image is colorized black. VectorCraft's own rule is that the pixels
that open black with ImageMask 0 take the color and the others are
transparent. A linked image (`XF`) keeps the same color for when its
file is relinked. 8-bit gray, RGB and CMYK images, and images with
ImageMask 0, open as before.
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.
In the legacy format, area type whose story flows through several linked
areas has one text path (`Tp` ... `TP`) for each area, each with its own
matrix and frame (version 7 specification, sections 15 and 15.5.3). The
reader kept only the first frame, placed it with the text object's last
valid matrix, and put the whole story in that one frame.
Each linked area with a frame now opens as its own area type object,
placed with its own matrix, and the frames are threaded in file order.
The first frame holds the story: the characters after every `TP`, and
the overflow text. When the file opens, the story is laid out again
through the thread (section 15.1 lists the flow from area to area as
final-form information). A linked area without a frame is left out.
Area type with one area opens as before.
The dictionary that follows a text object (its opacity mask, text wrap,
name and other keys) went to its one object. Its opacity mask and text
wrap now go on every frame, and its other keys on the first frame.
Point type is read with its first text path's matrix, and type on a
path with the matrix and start of the first text path that has a path.
Area type with no frame at all opens as point type with its first text
path's matrix. Before, these used the last valid matrix and, for type
on a path, the last start.
One text object reads at most 4,096 text paths. The number of frames
times the story's character count is kept to 4,194,304, so a longer
story keeps fewer frames. Frames past either limit are left out, with a
warning.
A legacy .ai in the version 7 form names a linked image's file with
`(path) modified XG` and draws the image with `XF`, which takes the
operands `XI` takes and has no samples after it (Adobe Illustrator File
Format Specification 7.0, sections 8.2 and 8.3). The reader ignored both
operators, so the image was dropped without a warning.
- `XF` after `XG` becomes a linked image of that file, its pixels placed
as an `XI` image of the same operands is placed. The file keeps no
pixels of it, so a placeholder stands in until the file is relinked in
the Links panel, with a note in the open result. No pixels are read,
so the image is not held to the pixel limit of `XI`'s samples.
- An `XF` without a file name before it, or with operands `XI` couldn't
read (no matrix, too few numbers, no pixels, or samples of other than
1 or 8 bits), is left out with the existing "an image VectorCraft
can't read was left out" warning. An `XG` applies only to the image
after it in the same raster object; `() 1 XG` before an embedded `XI`
keeps that image embedded.
- A PostScript page draws nothing for `XF`, so there the placeholder's
area is left out of the comparison of the layers with the page, and
the file keeps its layers. A PDF part draws the image's pixels, so a
file with one is compared with it there as before, and its layers are
not used without the PDF part.
- Linked images of different files get image keys of their own even
when they show the same pixels (the placeholder, or two identical
placed-file previews), so updating or exporting one file's image
reads that file only.
- `vectorcraft_doc::links::missing_placeholder` draws the placeholder: a
light tile, crossed out, as SVG import shows for a missing file.
Tests: reader tests for the placement, the image keys and the cases left
out, an end-to-end test of a legacy file with a linked image, a test of
a file with a PDF part, and a fuzz case for damaged files with `XG` and
`XF`. The new note has a row in each catalog that translates the
placed-file note.
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 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.
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.
When the layers fail the comparison with the file's own page because some objects draw
differently (an object in another color, or one that only one side draws), the import finds
each top-level object whose bounds hold a patch of such pixels (the smallest one that holds it)
and replaces it with the page's art in its bounds, in the same way as an unread object. It then
compares the layers with the page again. When that comparison passes, the layers stay, and a
warning names the layer or sublayer of each replaced object (`<Layer>` for one without a name)
and how many were replaced on it. Like the warning for unread objects, this warning counts as
a loss, so an export or Save As to the file's own path needs `acknowledgeLoss: true`. A
replaced wrap object keeps its wrap options, and area type is wrapped again around the page's
art. Placed files at their other placement are tried too when the first placement can't be
kept.
The file still comes in as its PDF part, with the same reason, when only edges or type differ,
when a difference lies outside every top-level object, when more than half of the top-level
objects (or more than 64) hold one, when such an object is or holds type or holds a wrap object
inside a group, when the page's type of a text object that stays would be copied into a
replacement, when the page's art can't replace an object for the reasons it can't replace an
unread one, when matching the differences to the objects would take too much work, or when the
second comparison still fails. Trusting the editing data keeps the objects as read.
File > Open and File > Place read BMP files named .dib or .rle (run-length
encoded bitmaps included), Photoshop files named .pdd and PNG files named
.pns, in any letter case. The Open and Place dialogs list them in "All
readable files", "All placeable files" and their format's own filter, and
Glass's Load Texture lists them too. Linked files with these names update
and relink, the Links panel shows their format, and Edit Original opens
them.
The extensions are read-only: exports still write .bmp, .psd and .png, and
an export to a path with one of the new extensions is refused as before.
The desktop packaging does not associate them with the app.
A graph in a legacy .ai, or in a current file's editing data, is written
as `%_Gs`, its data and settings on `%_` lines, its drawn art in a group
with a `%_... Go` tag after each object, and `%_GS`. The reader stopped
on the graph operators as ones it doesn't know, so such a file did not
open through its layers.
The reader now leaves the graph's records out with their operands and
reads the drawn art as usual: the bars, lines and labels come in as the
groups, paths and type they are written as. Paint operators on `%_`
lines between `Gt` and `GT` set nothing. One note per file says the
graphs came in as plain art without their data or settings. A graph
that starts inside another, or doesn't end before its layer does, keeps
the art read so far with a second note. Graph operators outside a graph
stay unknown.