The legacy reader skipped `TV`, `Tv`, `Ty` and `TY` of section 15.3.4 of
the version 7 file format specification, so type set with `1 TV` opened
horizontal.
- `TV` sets a text object's writing direction. VectorCraft keeps one
direction for each text object, the one in force at its first
character, with a warning when a later character has another.
- `Ty` marks a Kumi block as a Tate-chu-yoko run, which is VectorCraft's
own mapping, and `TY` does the same in horizontal type. A `TY` block in
vertical type opens unmarked. Two blocks with nothing between them open
as one block.
- `Tv` sets Standard Vertical Roman Alignment.
All four are modal, and an operand other than 0 or 1 leaves the value as
it was.
Docs: docs/file-format-parity.md.
An object read as its drawn look counted in the import's "came in as
their drawn look" note when it was on the page or in a kept symbol, but
not when it was in a pattern's art or a brush's art. A file whose only
such objects were in a pattern or a brush opened without the note.
- A kept pattern's drawn looks count, once for each pattern name: a
definition that a later one of the same name replaces no longer counts.
- A brush's art counts once a brush that is kept uses it; art no kept
brush uses adds nothing.
Q restores the painting state but not the transparency (Xy): the writer
sets Xy only when the next object's state differs from the last one it
wrote. Restored at Q, type after a clipping group following a Screen glow
drew in Screen and nearly vanished on a light background.
A blend whose spine was never edited has only its keys in the editing
data, no spine part. Its spine is the straight lines between the keys'
centres in the record's order; read as such, the blend comes in live
instead of as its drawn steps.
Illustrator 24 writes its symbol definitions ahead of %AI9_BeginArtStyles,
so a styled object inside a symbol found no style and came in as its drawn
look. The pre-pass that finds swatch names now also keeps the first
top-level art styles section, which the reader parses before the art.
An image over the pixel cap was left out without reading its data, so
the program went on to read that data as PostScript and the file failed
to open (Ghostscript's eps2write writes a page with a large inline image
this way). The data is now skipped: the filters that find their own end
(ASCIIHexDecode, ASCII85Decode, SubFileDecode) read the file up to it,
without decoding what they pass.
cvi and cvr read a string as the PostScript syntax reads a number
(PLRM 3rd ed., cvi, cvr), so (16#20) cvi is 32. The reader parsed strings
as decimal only. Ghostscript's eps2write prolog undoes a name's #20 escape
that way, so its files with a spot colour named with spaces stopped
reading at that colour.
The count of a SubFileDecode filter bounds what it passes through, not
what it allocates, but the reader refused counts over 4 MiB as a
rangecheck. Ghostscript's eps2write files read each content stream with
currentfile Length () /SubFileDecode filter, so a page whose stream is
longer than 4 MiB failed to open.
bind makes each procedure it goes into read-only and ignores read-only
procedures (PLRM 3rd ed., bind). The reader bound them again, so a prolog
that binds a procedure holding an earlier-bound one, after defining a name
such as M as an operator, turned that procedure's own variable M into
setmiterlimit. Ghostscript's eps2write prolog does this, and its files with
sampled functions (transfer, black generation) failed to open.
The reader put an opacity mask's art in a group every time, so a mask
made from one path came back from a saved .ai as a group holding the
path, while Make Opacity Mask keeps one object as the art. This also
changes how existing files with single-object masks open, because
the editing data of a .ai, an EPS and a PDF goes through this reader:
the art is the object.
A hidden object or a guide stays in a group, because the canvas draws
the art's root whether it is shown or not and whether guides show. A
clipping path alone stays a clipping group.
The editing copy writes a plain group of one object as a group (u, the
object, U), so it reads back as a group. Its other plain groups are
still written as their members. Hidden mask art is written shown,
because the canvas draws it and a group read back would not.
Clipping paths in a compound path that holds a drawn look become one
compound path. It took the paths' shared stroke, opacity and blend mode
but dropped the knockout shape flag. It now has the flag when every
clipping path has it.
An `Xy` whose shape operand is set now reaches an image (embedded or
linked) and a gradient mesh when it is written before the object, and a
mesh or blend when it is written after the object's end. A live blend
takes the flag from its drawn steps. Before, the reader dropped the flag
on these objects, so an image saved with Opacity & Mask Define Knockout
Shape lost it on reopening, with no warning.
A styled object takes the flag written on its drawn look. For a text
object, the flag goes to the object when the look draws only its type,
and otherwise to the group that holds the look and the type. An opaque
look that draws more than the type and sets the flag now goes into that
group as well.
Area type whose open frame ends where it starts was written as text and
read back in a closed frame. It is now written as its outlines, alone or
as a frame of a thread.
The reader gives an Xy right after a compound path or a text object to
that object. The writer now writes XR before the Xy of type, images and
guides, so type next to type, a compound path before type, image or
guide, and type before an image or guide keep their own opacity, blend
mode and knockout shape flag.
- Type keeps its Isolate Blending and Knockout Group state in a state
block after its text object, as its outlines did on their group.
- The reader no longer gives a layer the Xy before its last text object.
- The ASCII85 end marker of the text document stays on one line.
- The warning and docs/file-format-parity.md also list area type in an
open frame, type on an open path whose ends meet, type of only white
space and threads of type on a path as written as outlines.
- README.md, ROADMAP.md, docs/gaps.md, docs/roadmap.md and
docs/target-app-parity.md describe the editing copy a saved .ai holds.
- clip_path's doc comment is back on clip_path.
- import_fuzz reaches threaded area type and vertical type.
The editing copy of a PDF-compatible .ai wrote every text object as its
outlines. Point type, area type and type on a path are now written as
text: a text object in the layers that names its story, and the text
document after the document data (%AI11_BeginTextDocument) holding the
stories.
- Each story holds its characters (a paragraph ends in \r, a forced line
break is \x03), its character runs (font, size, leading, tracking,
baseline shift, horizontal and vertical scale, solid fill and stroke
with its weight, capitals, Tate-chu-yoko) and its paragraph runs
(alignment, indents, space before and after, hyphenation, right-to-left
direction).
- Point type keeps its point and, when it is turned, its matrix. Type
scaled by one factor, such as all type on a large canvas, has its scale
in its sizes, frame and path. Area type keeps its frame, rows,
columns, gutters and first baseline, type on a path its path and where
it starts and ends, and vertical type its orientation. A threaded story
is one story through its frames in order. Area type and type on a path
carry their frame's path in the layers as well.
- Type the text document can't hold is written as its outlines, with a
warning: type in opacity masks, pattern tiles and art cut to a clipping
object, mirrored type, turned or unevenly scaled area type and type on
a path, frames of several parts, area type that flows around a text wrap,
threads whose frames differ in orientation or include a frame written
as outlines, type and stories past the sizes the reader reads, and type
with inline graphics, ruby, gradient or pattern paint or an appearance
or opacity mask of its own. Other character and paragraph attributes are left out, with
a warning.
Tracking is written as a whole number of thousandths of an em, and
solid paints as simple paints; tracking that isn't a whole number is
rounded, with the warning.
Each construct is written in the form VectorCraft's .ai reader reads
(crates/eps/src/import/ate.rs at 36ef34dfdd: Texts::read :407, story
:501, frame :456, para :562, style :588, paint :622, AreaFrame::read
:284; the text object in import/ai/read/art.rs:15 and its path in
import/ai/frames.rs:26), and the tests read the written data back
through that reader. The scripting
reference documents the same attributes on the reading side (text frame
kinds, columnCount, columnGutter, rowCount, startTValue, endTValue,
orientation, and the character attributes):
https://web.archive.org/web/20160410101607/https://wwwimages.adobe.com/content/dam/Adobe/en/devnet/illustrator/sdk/CC2014/Illustrator%20Scripting%20Reference%20-%20JavaScript.pdf
The reader now closes a text object's frame path painted with a closing
operator (n, f, s, b, h), so area type's frame comes back closed; places
point type whose story keeps no composed lines at its frame's point;
keeps a story's last empty paragraph (only the \r that ends the story is
dropped); gives type made from the text document its text object's lock,
as it gives it its visibility; and gives type its text object's knockout
shape flag, as it gives it its opacity and blend mode.
Tests: the round trip of every kind of type and its state, a second
write of what is read back giving the same bytes, the text document and
stubs, scaled point type, a story split between threaded frames, the
fallbacks and the warnings, and the engine's editing copy with every save
option; a fuzz case for damaged text documents and stubs.
Docs: docs/file-format-parity.md.
A path with an Art brush comes in as the path, with the brush on its
stroke at its weight, instead of as its drawn look, when the brush
stretches art of solid-filled paths along the path, unflipped, at 100% in
Tints. The art runs on the side of the path the file draws it.
The drawn look stays for other colorizations and widths; Proportional,
between-guides and flipped brushes; type; a path with settings of its own,
a variable width, effects, a swatch stroke or an entry that isn't read;
and art with strokes, gradients, patterns, masks or effects, or no length.
A brushed stroke, Calligraphic ones too, takes the brush the Brushes panel
keeps under its name, and an image with a brush keeps its drawn look.
In every document, Tints and Tints and Shades take art in black ink alone
by its ink amount, and a process or gray stroke color's tints and shades
stay in that color's model.
Place and Expand bring the brushes the art uses, renaming on conflict.
Part of #637.
When Distort Appearance is on and the size a `.ai` saves for an
envelope's contents (`AIDeformArtWidthKey`, `AIDeformArtHeightKey`)
matches the contents' bounds with strokes, the reader now maps the
contents from that stroked box. It used to map them from the geometric
bounds, half a stroke inside that box, so an envelope of stroked art
could differ from its drawn art and keep the drawn art with a note.
Envelope Options hold the box's margins as `boundsMargin`, shares of the
geometric bounds at the top, bottom, left and right. Native files write
them only when they are not zero, and `object.envelope.info` reports
them then. Leaving Edit Contents measures them again when the strokes
reach differently than when Edit Contents began, and the node holds
what they measured then until it leaves. New envelopes have no
margins, so their results do not change. Contents with no width or no
height, such as one straight line, get no margins.
Docs: docs/mcp.md, docs/target-app-parity.md.
When the scan for swatch names searched data outside a section and found no `~>`, it stopped searching for the end of all later data. Data in a later section then was not skipped, so a `(` on a `%_` line of it opened a string that ran past the section's end marker, and a color named after the section did not reserve its name. The scan now records the search that found nothing per section's end marker, so a search in another section still stops at that section's markers.
Data with no `~>` before the section's end marker was lexed as tokens, so a `(` on a `%_` line of it opened a string that ran past the marker and the section did not end. The scan now skips that data up to the section's end marker, or up to the start of a section of the same kind nested after it. A `%` on the key's own line starts no marker, and a marker line with bytes that are not UTF-8 counts, as the scan reads comments.
When versioned content does not end, the scan for swatch names goes back to where its dictionaries stood at the start of that content, so data in the range it reads again is skipped.
A section scan lexed a /ForeignObject's /Data, a /Binary dictionary's data and a text document's data as tokens. A `%_` line of that data holding a `(` opened a string, so the section ran past its end marker: the editing data failed to read, or the art after it was misread. Reader::section now skips the data up to its `~>`, as the reader does for art outside a section. Data without its `~>` before the section's end marker is not skipped. The scan for swatch names does the same.
Comments only. A saved Soft Mix holds Command 11 and a Mix of the Mixing
Rate divided by 100, and a saved Mix can fall outside the 0.01 to 1 the
reader takes, which keeps the drawn look.
A group whose style holds Effect › Pathfinder › Intersect, Exclude, Subtract,
Minus Back, Divide, Outline, Trim, Merge, Crop, Hard Mix or Soft Mix now opens
with its members editable and the effect live, as Add already did
(https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/combine-objects-using-pathfinder-effects.html).
The Command numbers 1 to 10 are VectorCraft's own reader rules, learned from
saved files. Soft Mix's number 11, and reading its Mix from 0.01 to 1 as a
Mixing Rate of Mix times 100, are also VectorCraft's own reader rules.
These still open as the drawn look:
- Trap, and the effect on a path, on type, on a layer or on a fill or stroke.
- Any operation whose saved Pathfinder Options (Precision, Remove Redundant
Points, Divide and Outline Will Remove Unpainted Artwork), ConvertCustom,
Mix or Trap settings differ from the values the reader accepts, although
only Soft Mix uses Mix and none of these operations uses the Trap settings.
The import fuzz test now also gives the style every option with any Mix, and
checks that a live Soft Mix's rate is from 1 to 100.
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.
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.
Each definition's reader started with fresh blend layout and envelope
check budgets, so a file with many symbols, patterns or brush art
sections got those limits again for every definition. The reader of a
definition now takes the import's remaining budgets and hands back what
is left after reading, whether the definition is kept or not.
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.
A .ai or EPS graphic style library holds graphic styles and their list
in its editing data but no layers, so it came in as its PDF part or
page with the default styles, and Other Library listed those. A file
read as its PDF part or page, because it has no layers or its layers
can't be used, now gets the styles of its list, in list order, as a
file read from its layers does: File > Open shows them in the Graphic
Styles panel and Other Library lists them in a library panel.
Style stroke weights and effects are scaled by the Large Canvas factor
of an EPS, and for a .ai whose editing data is in the units of its
pages by the pages' UserUnit or the declared Large Canvas scale.
Part of #3349
A brush's art is read by a reader of its own, which was given the
patterns defined before it but not the record of which of them use the
version 7 form. A fill in that art with such a pattern then ignored its
offset, scale, rotation, reflection and shear operands, and placed the
tiles by the matrix alone. The brush-art reader now receives that record
as well, so those operands place the tiles. A brush whose art has such a
fill without all nine operands is now not read, as other art the reader
can't read whole isn't; before, the brush was kept.
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.
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.
In `.ai` and EPS editing-data import, a clipping group, layer or opacity
mask's art that holds more than one clipping path (`W`) now clips to the
intersection of the paths, as each `W` intersects the current clip (AI7
spec section 5.6.2,
https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf). Before,
the paths were joined into one compound path, which clipped to their
union, and kept a stroke only when every path had the same one.
Each clipping path after the first clips a clipping group nested inside
the one before it, and keeps its own stroke, opacity and blend mode. The
innermost clipping group holds the art in the order written; as before,
every object in the group is clipped by all of its clipping paths. A
clipping path's painted fill stays at its path's level. A compound path
of clipping paths (`*u` ... `*U`) is still one clipping path. Clipping
paths that would nest the art past the document's 32 levels stop the
editing data from being read.
Each gradient definition that no swatch names took its name by trying
`Gradient`, `Gradient 2`, `Gradient 3`, ... from the start, so a document
with many such gradients tried every earlier name again for each new
one. The writer now keeps the number the next search starts from, and
still skips names that a gradient, swatch or pattern already has.
- A test names 1,000 gradients around two taken names and checks that
each name is tried once (1,002 tries; 502,496 when each search starts
over).
- An export test writes 300 gradients next to a `Gradient 3` swatch and
a `Gradient 7` pattern, and checks the names in order and the same
output when written again. It passes with the old search too.
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.
Type in the text document of a .ai or EPS file's editing data that names
no character or paragraph style, a story without style runs included, is
set in [Normal Character Style] and [Normal Paragraph Style]. The reader
took what such type leaves out from the text document's base attributes
instead, or built-in defaults for a story without paragraph runs, and it
did not read paragraph styles.
A Large Canvas file (1:10) holds the normal styles' lengths at the art's
scale (1.2 for 12 pt) and the base attributes' at the page's (12). The
reader scaled the base attributes' 12 by 10, so the type came in at
120 pt. In an EPS the page comparison then rejected the layers, and the
file opened as its printed page without its linked images.
In any file whose normal styles differ from the base attributes, such
type now takes what the normal styles set, such as font, size, colors,
stroke, alignment, indents, space before and after, and hyphenation.