In a Separation color space, the colorant name All marks every plate and
None marks no plate (ISO 32000-1:2008, 8.6.6.4). A spot swatch with one of
those names was written under its own name, so its paint printed on every
plate or on none.
PDF export now writes such an ink as All Spot or None Spot, numbered
All Spot 2, All Spot 3 and on when a spot swatch already has that name,
the same rule EPS export uses. The new name is used for fills and strokes,
tint shadings, DeviceN gradients, colorized images and the Lab alternates.
Registration still writes All.
With Create PDF layers on, every layer's form XObject had
/BBox [-32767 -32767 32767 32767] and started with an empty clip at
x = -32000 that only marked the form while the file was written. Other
apps read that as art about 23 m across, and the reporters could not move
or place the imported layers.
- The marks of layer, overprint and invisible-text forms are removed when
the file is finished.
- A layer's form gets a box round its art and the page's paper, in whole
units. Since the box still covers the page, PDF import skips it as a
clip, so sublayers still import as sublayers.
A gradient of one spot ink's tints and paper white is written as a
Separation shading in that ink, with each white stop at 0% of it. A
white stop linked to a swatch that is not a spot color (a global White
swatch, for example) did not count as paper white. In an RGB document
the spot ink was then dropped: a Classic gradient was written in the
stops' own colors, and a Perceptual one was exported as an image in
process colors.
Such a stop now counts as paper white in the Separation and DeviceN
paths and in the checks that pick them, as an unlinked white stop does.
A stop linked to a white spot color is still a stop of that ink.
A Perceptual linear or radial gradient that mixes spot colors with each
other or with CMYK or gray stops was written as an image in process
colors. It is now written as a DeviceN shading of its inks, with the
stops on its Perceptual curve: at each one, the inks of the two stops on
either side are mixed at the weight where the two colors mixed in
display RGB have the OKLab lightness of the color on the curve.
- Gradient::ink_mixes gives the curve's stops as weights between stops,
under the same 4,096-stop limit as ink_tints.
- Perceptual gradients that mix a spot color with RGB or Lab stops,
dithered fills and freeform gradients stay images, and the warning
now names those cases.
A Perceptual linear or radial gradient was always written as an image,
so a gradient of spot ink tints lost its plate, and no warning named the
spot ink.
PDF export now writes a Perceptual gradient whose stops are tints of one
spot color (and paper white) as a Separation shading in that ink. Its
tints follow the gradient's curve: at each stop the renderer places on
the curve, the tint with the OKLab lightness of the color there.
Dithered fills, freeform gradients and Perceptual gradients that mix a
spot color with other colors are still images, and the export now warns
when such a gradient has stops linked to a spot swatch that its spot
colors are written in process colors.
A linear or radial gradient whose stops mix a spot color with process
colors, or two spot colors, was written in process colors with a
warning. It is now written as a DeviceN shading of the process inks
its stops use and its spot inks, so each spot ink separates onto its
own plate. An off-center midpoint becomes a stop with the inks halfway
between its two stops. A gradient that mixes a spot color with RGB or
Lab colors is still written without the spot ink, with a warning that
says so.
- A composite (Type 0) font is noted by its descendant CIDFont's BaseFont
(ISO 32000-1 9.7.4.1), not its own BaseFont, which ends in "-" and the
CMap name (9.7.6.1, e.g. Name-Identity-H); without one, the CMap name is
taken off the end of the BaseFont.
- A font name's family is the longest part before a hyphen (or the comma of
Arial,Bold) that names an available family, so Noto-Sans-Bold is Noto
Sans, Bold and Noto-Sans-CJK-JP is Noto Sans CJK JP; a shorter family
still matches when no longer one is available.
- A missing font is split only at a comma or before the first part that is a
style word (Helvetica-BoldOblique); otherwise the whole name is its family
(MS-PGothic is reported as MS PGothic, not MS).
Synthetic tests (no host fonts): Families::of builds the family list.
# Conflicts:
# docs/file-format-parity.md
# docs/gaps.md
# docs/type-parity.md
An unclipped opacity mask is drawn on a white backdrop. When an image in
the mask art could not be written (a colorized image whose color channel
is over the size limit, an image that can't be decoded, or one placed
beyond the coordinates a file holds), the backdrop stayed white and the
masked object showed in full where the image should hide it.
The exporter now records an image left out while it writes mask art,
and leaves the masked object out instead of applying the incomplete
mask, with a warning. A failure in a nested mask makes the outer mask
incomplete too.
colorized_image returned before its cache insertion when the image was
missing, left out or undecodable, or when its color channel was larger
than MAX_RASTER_ALLOC. Every placement of such an image then decoded it
and computed its coverage again. Both cases now cache None for the key,
as load_image does.
The import read each form's /Group /I and /K from the file in order and
matched them to the transparency groups the interpreter pushed. The
interpreter draws a group that adds nothing (full opacity, Normal, no
mask, neither flag) straight onto its parent without pushing it, so every
group after one took another group's flags. Blend steps written as a
knockout group of falling opacity then painted over each other and drew
as solid dark shapes instead of light rings.
The interpreter already reports the flags right after pushing a group
(set_knockout, set_isolated); the import now uses those and drops the
file-order list. That list was capped at 10,000 entries, past which a page
failed to import; that limit goes with it.
A colorized image's color channel is the paint's samples repeated once a
pixel, and nothing bounded its size: an image colorized in Registration in
a document with many spot swatches could allocate without limit, and an
overflowing length panicked inside repeat. The length is now computed with
checked arithmetic and refused above MAX_RASTER_ALLOC, the image's own
decode budget, and the export leaves such an image out with the existing
"an image couldn't be decoded" warning instead of drawing it uncolored.
Patch and triangle mesh shadings of more than 4,096 patches, and strokes
painted with a mesh shading, came in as a flat grey. Illustrator writes
meshes of up to 14,355 patches, and writes a freeform gradient on a
stroke as a stroke painted with a type 7 shading.
- MAX_PATCHES is 16,384, as in the .ai editing-data reader.
- A stroke painted with a mesh shading is the meshes clipped to the area
the stroke paints (stroke_outline: width, caps, joins, miter limit and
dashes, built in the path's space and then transformed); draw_meshes
always clips a stroke's meshes.
PDF export (and the PDF-compatible .ai it writes) cut each gradient mesh
patch into 8 x 8 flat-coloured quads. Viewers showed colour bands and
hairline seams between them, and files with many meshes became very
large and slow to write.
An opaque mesh is now written as one Type 6 (Coons patch) shading
(ISO 32000-1, 8.7.4.5.7) filling its outline, through the PDF writer's
MeshGradient: each patch blends its corner colours bilinearly, as the
canvas draws it. Colours go through page_color as other fills do, and a
mesh mixing RGB with other models goes through RGB. A mesh with
see-through points, an invalid one, or one past the coordinate range
keeps the quads.
Pages that share one Contents array made the name of a refused stream read that array once per page, quadratic in a crafted file. Each object is now read once, and a page's own array as before. From the review of #4612.
A page's Contents can be an array written as an object of its own (/Contents 6 0 R). Its streams were named "a stream in it": the reference was compared with the stream, not the array's items. From the review of #4603.
A page drawing one form over and over made its art again for every drawing
with no limit (a 1 KB file took 877 MB), and a form decoding to 64 MiB drawn
a thousand times hung. The import now makes at most half the objects a saved
document may hold (2M, 256K on the web), leaving the rest out with a warning,
and the scan reads at most 2 GiB of form content per page before refusing it
as past the reader's limits.
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.
PDF export writes a CMYK image whose opacity mask is a gray image on its
own pixel grid with the mask as the image's soft mask. That path always
asked viewers to smooth the image, so a pixelated piece of an envelope
with Anti-Alias off was still written with /Interpolate when it had such
a mask. It now takes the pixelated flag as images without a mask do, and
caches the smoothed and pixelated versions apart.
Envelope Options stored Anti-Alias and Preserve Shape Using, but nothing that
draws an image read them: a distorted image was always smoothed and always
cut into clip groups.
- With Anti-Alias off, each piece of a distorted image is marked pixelated
(saved as `pixelated`). The renderer draws it with nearest-neighbor
sampling and without mips, SVG export writes `image-rendering: pixelated`,
and PDF export writes it without /Interpolate.
- With Preserve Shape Using Transparency, each piece is the image with a
white opacity mask in the piece's shape instead of a clip group. The
renderer paints an image whose mask is one plain white path straight into
that path, as it does for an image in a clip group, when the path lies
inside the image's frame.
Docs: docs/mcp.md, docs/target-app-parity.md.
An axial or radial shading's stops are the samples that linear
interpolation does not reproduce. An ink that changes nothing in RGB,
such as a varnish, could paint at dropped samples only, and its spot
swatch was then missing. Every sample now marks the inks it paints with
as used; the stops are unchanged.
A DeviceN color with a spot ink at a tint above 0 together with a process
plate or a second ink imports as RGB. The importer returned before marking
those inks as used, and only used inks become spot swatches, so the spot
ink was missing from the document and from every later export. Shading
stops take the same path, so a DeviceN gradient lost its ink the same way.
The art still imports as RGB with the existing warning; each ink it paints
with now becomes a spot swatch with its 100% color.
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.