dataset.export checked only the 1 MiB size, so a document with more than
1000 data sets or 256 variables exported a CSV that dataset.import then
refused. Export now refuses such a document with a message naming the
limit, and the messages have rows in every complete-message catalog.
Undo, Redo and a canceled undo group put back an earlier document but
left the Variables panel highlight as it was. After renaming the
highlighted variable and undoing, the highlight still named the new
name: a variable defined later under that name showed highlighted
without a click, and Delete removed it.
Each of these paths now clears a highlight whose variable the restored
document does not define, as Delete Variables and Load Variable Library
already did.
Legacy type (`To` ... `TO`) now reads `min opt max TC` and `min opt max TW`
into the paragraph's letter and word spacing, `hangingQuotes Tq` into Roman
hanging punctuation, and `hyphenate limit lead tail max Th` into hyphenation
on or off, After First, Before Last and the Hyphen Limit (0 when the limit
flag is 0). Values are kept to the ranges the Justification and Hyphenation
options take, and a spacing triple out of order goes back to its default.
Operands of the legacy type attributes are read by their place: an attribute
whose operands are missing, are not numbers, or are flags other than 0 and 1
stays as it was. Before, a value that was not a number shifted the later
operands (`5 /x 7 Ti` set indents of 5, 7 and 0), and a bad `TA` or `Ta`
turned kerning on or set the alignment to left. One long `Tx` no longer goes
past the reader's limit on characters.
A curve rejected for having more than 256 segments now counts the most points a curve may have against the document's pressure points, so linking it from many strokes reads it a bounded number of times. Once the points are used up, later strokes import as plain strokes with a warning.
A version 7 text object has no arbitrary-angle character rotation: its
character attributes include baseline shift (Ts), scaling (Tz) and
tracking (Tt), Tv only turns Roman characters 90 degrees in vertical
text, and the positioning operators such as Tm are ignored on reading.
Type with Character Rotation was written as editable text with the
rotation left out, so rotated characters opened upright.
Such type is now written as its glyphs' outlines, the way type 8-bit text
can't hold already is, with the warning "type with rotated characters is
written as outlines, which keep the rotation, and is no longer editable
text". The outlines keep each character's rotation, baseline shift and
scale. Type without rotated characters, and type turned a whole number of
times, is still written as text.
Docs: docs/file-format-parity.md, docs/mcp.md, docs/target-app-parity.md.
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.
An override on a dynamic symbol instance names an object of its symbol's
art by path. Replace Symbol kept every override whose path the new
symbol's art also had, so a change made to one object of the old symbol
was applied to an unrelated object of the new one. An instance given a
different symbol now drops its overrides; an instance replaced with its
own symbol keeps them. Undo brings them back.
- Each segment of a pressure curve is also sampled where its width stops
rising or falling, so a dip between the regular samples of a long
curve is kept. The segment's values are scaled into -1..1 before that
is solved, so huge values in a file can't overflow it.
- A curve counts as full width only within 1e-9 of it, so a curve just
under full width keeps its profile.
- The pressure points of all strokes in one read are limited to about a
million, as for characters and table cells. Curves at full width count
too, and once the limit is reached no more curves are read; the
strokes past it are imported as plain strokes with a warning.
An Affinity stroke shaped with the Width tool stores its pressure curve
(LDeP: position along the path against width). The importer warned and
drew the stroke at full width all along.
- The pressure curve is read and sampled into the stroke's width
profile (2 to 8 points per segment, 256 segments at most, widths up
to the full weight). A curve at full width all along stays a plain
stroke; one that can't be read is warned about.
- Brush strokes keep their own warning.
- The pinned Affinity 3 signatures for the five corpus files with such
strokes are updated.
Dropping rows with neither a label nor a value read the data padded to
the widest row, so a dropped row wider than the rest (`,,`) left every
kept row one blank cell wider. The graph then had an extra blank series,
and a right-axis assignment to that index was kept. Each kept row now
keeps its own width. This applies to graph.setData and to a graph
variable's data set.
With the clipping path of a clip group or a clipping layer selected, or a
member of a compound path that is the clipping path, Paste in Back put the
clipboard's objects below it. The bottom pasted object then became the
clipping path. With nothing selected in a clipping layer, Paste in Back did
the same at the bottom of the layer.
Paste in Back now puts the objects right above the clipping path, as Send to
Back keeps art above it.
Applying a data set wrote a text value into the bound frame only, and the
thread's reflow then joined it with the other frames' old text. The art no
longer matched the row it had just applied, a captured row applied back lost
text, and a value bound to a later frame deleted the rest of the story.
- A text value bound to any frame of threaded text replaces the whole story,
which then flows through the frames again. `overflow` names the story's
last frame.
- Capture Data Set, Update Data Set and the Variables panel's match check
read such a frame as the whole story, frames joined in thread order.
- When any frame of the story is locked, the text value is skipped and
counted in `skipped`, and the story is not read.
- Two text variables bound in one story apply in definition order, as for
two on one object: the one defined last wins.
Docs: docs/mcp.md.
variable.lock's params text and the doc comment on Variables::locked said every
dataset command still runs while the variables are locked. dataset.import is
refused when its header would define a variable; both texts now say so, as
docs/mcp.md and dataset.import's params already do.
Choosing a data set gave a graph variable's CSV to the graph without the
two steps graph.setData takes after new data: rows with neither a label
nor a value were kept and drawn as empty slots, and a right-axis series
the new data no longer had stayed assigned, so a series added by a later
row started on the right axis. The data set now goes through the same
steps.
variable.rename and dataset.rename checked whether newName was taken before
checking that name exists, so renaming a missing name onto a taken one said
the new name already exists. Both now report the missing name first.
Version 7 files write each palette swatch as its paint and `Pc`, with no
name. The reader only kept a swatch with a name before `Pc`, so such a
palette gave no swatches and the document got the default set.
A swatch without a name is now named by its spot color, gradient or
pattern, or by its values in the format the swatch import already uses
for unnamed colors ("Gray K=25", "C=25 M=0 Y=0 K=0"). `Pn` and a `Pc`
with no paint before it add nothing. Named swatches keep their names.
An operator the reader does not know, or a `Pg`, now ends the paint
before it, so a later `(name) Pc` no longer takes that paint. The
swatch limit is kept with a running count instead of summing every
group at each `Pc`.
With a member of a compound path selected, Paste in Front and Paste in Back
put the clipboard's objects next to that member, inside the compound path,
whatever they were. A pasted group, compound path or type object then sat
inside a compound path, which holds only paths. The model-based test
`random_command_sequences` stops on this with "compound #N has a non-path
child".
When the clipboard holds anything other than paths, the objects now go
right above or below the compound path. Paths still go next to the member,
inside the compound path.
The color sliders now mark the row they edit with `model: "new"`
instead of a dialog-wide flag, and `recolor.apply` reads a row's own
`model` before the command's. Rows the sliders did not edit keep each
color's model, so with RGB art and a CMYK color group, a slider edit of
one row no longer turns the other rows' art CMYK. Another edit of the
row's new color, Reset and rows reduced again remove the mark, and
`recolor.randomize` moves it with its new color.
A wheel drag or a harmony rule on the Edit tab now removes a row's tint
when it gives the row another new color, as the color sliders, the hex
field and the Color Picker already do. With Limit to Library, an edit
that snaps back to the row's own color keeps the tint.
recolor.randomize moves each row's tint with its new color when it
shuffles the order, and removes the tint from a color that saturation
and brightness changes.
The Assign tab gets a Color Mode menu (HSB, RGB, CMYK, Lab, and Tint when
the row's new color is a spot color of the Limit to Library library) and
its sliders under the rows. They edit the first selected row's new color
and are disabled with no row selected. Once they have edited a color in a
mode, the preview and OK send recolor.apply with model "new", so the art
takes the color in that mode.
recolor.apply rows take an optional tint (percent, default 100): art linked
to the row's global or spot library swatch takes that share of its tint.
recolor.randomize keeps a row's tint.
Object > Plug-ins > Color Effects > Find Nearest Swatch... picks a
library (the document's swatches, a built-in library, or a library or
colour book the user saved or loaded) and Fill and Stroke, and lists
each colour of the selection with its three closest swatches and their
CIEDE2000 difference. Apply gives each colour its nearest swatch as one
undo step.
- addon.color.nearestSwatch (a query): the closest swatches of a
library to each colour of the selection's fills and strokes (solid
colours and gradient stops, inside groups and type), or to a colour
given, with deltaE; count 1..50.
- addon.color.applyNearestSwatch: recolours those paints (and gradient
meshes) to their nearest swatch; global and spot swatches link, and a
library's swatches the colours take are added to the document first
(as swatch.library.add adds them), so a spot colour stays spot.
- The library picker Image Trace's Document Library uses moves to the
Swatches panel module and is shared.
- No colour book data ships: only libraries VectorCraft computes or the
user loads are searched.
Each format's copy options are given in full, so no remembered option
applies: PDF and .ai copies write every page (range all), SVG copies
the art of every artboard in one file (useArtboards false), native,
.ai and EPS copies include linked files' data.
Each copy is saved with images embedded and artboards together, so a
document last saved with linked SVG images writes no image files beside
its copy; any other file a save reports is reserved for the copies still
to come.
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.
Baseline shift now moves a character in the line's frame, outside
Character Rotation, so a positive shift moves a character up from its
line at any rotation, and the character turns about its shifted pen
position. The Touch Type tool splits a drag into its part along the line
(kerning) and its part up from the line (baseline shift) at every
rotation; before, within about 6 degrees of a quarter turn it dropped
the cross-line part.
Characters that already combine a rotation with a baseline shift are
drawn in a new position. Their stored values do not change.
- text: glyph transform order, PositionedGlyph::pivot (the turning
point), tab leaders in the same order
- tools: Touch Type splits drags against the line's up axis and measures
rotate-handle turns about the shifted pen position
- engine: text.touchType description; dy is up from the line
- docs: mcp.md Touch Type paragraph, target-app-parity.md progress line
The Character panel's All Caps, Underline and Strikethrough buttons, and
its menu's All Caps, Underline, Strikethrough, Proportional Metrics,
Tate-chu-yoko and No Break, read the first selected character's style.
With only some characters marked they could show on, and choosing one
turned the attribute off for every character. They now show on only
when every selected character has the attribute, and a click on a mixed
selection turns it on for all, as Small Caps and Standard Vertical Roman
Alignment do. type.underline (Cmd+Shift+U) underlines a mixed selection
the same way. The Character Alignment and Digits submenus check no item
while the selected characters differ.
text.setFormat ignored a value that was not true or false for underline,
strikethrough, allCaps, proportionalMetrics, tateChuYoko,
standardVerticalRoman, noBreak, hyphenate, hyphenateCapitalized and
romanHangingPunctuation. It now refuses it with "`<key>` must be true or
false" and changes nothing, as text.setRangeStyle does.
An envelope in a .ai's editing data opened as its distorted art, with
its mesh and contents dropped. It now opens as a live envelope of its
contents: a warp with its style, bend, distortions and orientation, a
mesh with its points and handles, or a top object with its outline,
with Envelope Options, Fidelity and Edit Contents.
The envelope replaces its drawn art only when VectorCraft draws it like
that art, with a budget so that live envelopes don't make the file open
from its PDF part. When the layers still differ too much from the page,
the drawn art is put back. An envelope that can't be read, or that
draws differently, keeps its drawn art with a note. Envelopes of type
keep their drawn art and hidden type as before.
The editing data's layer options set a template layer from the flag four
places after the colour index. The colour index of nine-operand options,
and the colour index and dimming percentage of eleven and twelve operand
options, are read from their own places (they were read one place later).
A straight (N) * guide path directly on a layer that runs across the
whole canvas, or from edge to edge of one artboard, opens as a ruler
guide on that layer (an artboard guide of that artboard); other guide
paths stay guide paths. Guides scale with the document when its editing
data is in the units of a UserUnit page, and an EPS that keeps only its
first artboard keeps only that artboard's guides.
Fixes#779
addon.document.exportAllOpen {folder, format?} writes a copy of every
open document to one folder as Save a Copy does, each named after its
document with taken names numbered so no file is replaced. Documents
keep their paths and modified state, the active one stays active, and a
document that can't be written is reported while the others are still
written. Object > Plug-ins > Document Tools gets Export All Open
Documents as PDF and as SVG, which ask for the folder.
A new text_import_fuzz case builds a valid Word 97-2003 document (version
3 or 4 compound file, its streams in the mini stream or in FAT sectors),
checks that it places, and then damages the container itself: header
fields, the sector and mini sector shifts, FAT and mini FAT entries,
directory entry fields and stream sizes, single bytes, and truncation.
Session::execute turns a panic into EngineError::Internal, so the cases
in this file did not fail when an import panicked. They now go through
no_panic, as import_fuzz.rs does.