The spacing fitted from a type-on-a-path layout measured each turn against
the glyph's own foot. Skew's feet stay upright, so on a tilted or leftward
path the turn picked up a false angle and the fitted spacing was wrong (a
straight path at 30 degrees gave -9.7 instead of 0). Effects other than
Rainbow and 3D Ribbon now measure the turn against the baseline turned a
right angle.
The file-format doc states that a frame whose options set key 6 to true
keeps the default effect, alignment and Flip.
Type on a path from a file's text document opened as Rainbow on its
baseline, not flipped, whatever Type on a Path Options the frame kept.
Each frame now keeps its effect (Rainbow, Skew, 3D Ribbon, Stair Step,
Gravity), Align to Path (Ascender, Descender, Center, Baseline) and Flip.
A code or flag that can't be read leaves that option at its default.
Flipped type opens with the frame's path reversed once, after a copied
object takes its own art path, and an open path's end bracket stays so
the type starts where the file's range ends.
Spacing is still fitted from the file's layout. The fit now measures along
the path when Align to Path moves the baselines off it, and it follows the
glyphs' feet under 3D Ribbon, whose baselines stay level.
`rdp` measured each sample to the infinite line through the ends of the
line that would replace it. A sample beyond either end could be far from
that line segment and still be dropped: a line to (100, 0), a 0.2 pt
U-turn and a line back to (50, 0.2) became one line at a tolerance of
1 pt, leaving the tip 50 pt away. `rdp` now measures to the segment, so a
sample beyond an end is measured to that end, and a zero-length chord
measures to its point as before.
The test `a_path_that_turns_back_past_its_end_converted_to_straight_lines_keeps_its_tip`
covers that path.
The test's path zigzags by 0.1 pt. At level 100 the tolerance is 1/32 pt,
and a fit now stays within it, so the preview at that level leaves the
path as it was. Level 0's 32 pt tolerance takes the zigzag out.
Object > Path > Smooth weighed every sample within three spreads of each
point, so the work grew with the square of the anchor count on traced
paths: 360 steps per sample for 1,000 anchors and 2,949 for 16,000.
Consecutive samples within 1/16 of a spread of each other are now
weighed together as their count at their mean place, found through a
tree of sums over the run's samples. A traced outline takes 203 to 286
steps per sample from 1,000 to 64,000 anchors, and every smoothed point
stays within a thousandth of a spread of weighing the samples one by
one (300 random runs).
The curve fitter split a piece over the tolerance at its worst point and
accepted any piece at depth 41 whatever its error. A worst point near the
end of a piece peels off a few points, so a long path refitted nearly all
of its points at every level. A serpentine of 200,000 anchors kept pieces
with a fitted error of up to about 1,598 pt at a tolerance of 1 pt.
Simplify also took at most 256 samples per segment, so the lines between
the samples on a long arc could be farther from it than the tolerance.
- `fit_rec` moves a split that is near an end of its piece to two fifths
of the piece from that end, so the depth is logarithmic, and it keeps no
piece over the tolerance. A piece of 32 points or more is first fitted
to about 16 of them, and it is split when that fit is over the tolerance.
- A curve is also rejected when it is over the tolerance from the lines
between the points it fits, with samples close enough that it stays
within 1% of the tolerance of the chord between them. A pair of points
that would need more than 1,024 samples counts as too far.
- `join_pieces` joins neighboring pieces when one curve fits them, up to
33 pieces per curve.
- `sample_steps` takes enough samples of a curved segment to keep it within
0.5% of the tolerance of the lines between them. A segment that would
need more than 65,536 samples stays as it is, and so does a segment
after a run's first 16,777,216 samples or 2,097,152 result segments. With
Convert to Straight Lines such a segment becomes evenly spaced lines, up
to 65,536. A fit holds at most 1,048,576 samples, so a longer run is
fitted in parts.
- Simplify fits at 98% of the tolerance, since turning a nearly straight
curve into a line moves it by up to 1.5%.
- Above slider level 0, Object > Path > Smooth fits a whole closed path
with no corner along one direction across its start, when no segment
stays as it is and the path is fitted as one part. A direct-selected
section does not get this. The start can still have a kink: a first or
last fitted curve that is nearly straight becomes a line, which leaves
along its chord, and a refit with more anchors than the path is
discarded, so the path stays as it is.
A closed subpath that refits to one curve is still dropped, as on main, so
it can end up farther than the tolerance from the result. docs/mcp.md
states this.
The anchors of every simplified path can differ from before, and so can
the results of the Pencil, Blob Brush and Smooth, which use the same fit.
Session::execute ran check_params only in the engine's own tests, so a
direct execute in a release build ran a misspelled key with the
command's default. It now checks every call, the commands a command runs
included, as MCP and the control port already did. A command.batch or
an action.batch is checked before its first step runs, and each step is
checked again as it runs, so an effect.setParams step is checked
against the effects the steps before it added.
Saved steps play through Session::replay, which takes keys a command no
longer reads, so an action recorded by an older version still plays and
Session::replay still plays an older journal. The Actions panel's Play
(Accelerated, Step by Step and Pause For, its function keys and
ui.action.play) and the Batch dialog use it.
Docs: the cmd/params.rs module doc, the Parameter keys section of
docs/mcp.md and a line in docs/target-app-parity.md describe both
paths.
About > Contributors still showed an old snapshot, because nothing
regenerated contributors/contributors.json before a release. Opt in to
the shared refresh from craft-common-actions: a read-only credits job
regenerates the snapshot for the release commit, and the shared release
workflow installs it before every source build. The snapshot is not
committed back.
request, execute, commands and screenshot move onto the shared crate (storytold/craft-libs#413): a timeout on every call, the screenshot's PNG also in data, and docInspect and uiInspect beside them. A call the page gave up on gives up its request if the app has not started it, so the app skips it. openFile, captureDownloads, files, readFile and inspect stay VectorCraft's own, on the same object.
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.
After Select All in threaded text, the Justification dialog kept the edited
frame's selected range when that frame was no longer threaded at OK. A thread
broken after the frame moves the rest of the story into it, so the paragraphs
past the old range kept their values. The dialog now drops the range and
changes every paragraph in that frame.
With mixed indents, the Tabs panel sends one text.setFormat per paragraph,
addressed by frame and byte offset. Threaded text reflows after each step,
so an indent that made earlier paragraphs wrap onto more lines moved text
into the next frame, and the later steps landed on the wrong paragraphs or
on none. The steps now run from the story's last paragraph to its first, so
each step's offset comes before every change already made.
After Select All in threaded text, the Tabs panel's indent markers read
the indents of the edited frame alone. When another frame's paragraphs
had a different left indent, a marker drag changed them by the edited
frame's values, or left them alone. The markers now take every
paragraph of every frame in the story, so each paragraph keeps its own
left indent or first-line position.
The Justification dialog kept the frames of the story from the moment
it opened. OK now acts on the frames in the edited frame's thread when
it is pressed, so a frame that left the thread while the dialog was
open is not changed, and a frame that joined it is.
After Select All in threaded text, the panels read the frames from the
list saved at Select All. A frame that had left the thread still took
Paragraph, Tabs, Justification and Text Styles changes, a frame added
later did not, and when the first saved frame had left, Character,
OpenType and font changes went to that frame alone.
text_story now reads the frames of the edited frame's thread from the
document. Locked and hidden frames stay in the story.
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.
set_refuses_a_recovery_or_plugins_folder_it_cannot_use takes write or
list permission away from two folders. A guard now gives each folder mode
0o755 when it is dropped, also when an assertion panics, so the test
folder is removed afterwards.
prefs.set did not check the folder given as recoveryFolder or pluginsFolder,
beyond the automation roots check, so a control or MCP caller could save a
folder that does not exist, a file, or a folder the app cannot use. Only the
Preferences dialog checked the folder itself.
The dialog's folder checks move to the engine, and prefs.set runs them after the
automation roots check. A folder that fails returns the same error the dialog
shows, and no preference changes. An empty value, an omitted value and a folder
the preference already holds are not checked. The Wasm build skips the check, as
the dialog did. The dialog's OK now gets the check from prefs.set, and Choose…
calls the moved check directly.
Enter and Escape counted their key repeats as presses. Holding Enter with
a selection and a selection tool opened the Move dialog, confirmed it,
and opened it again at the key's repeat rate. After a drag, which gives
Move an offset, each round moved the object again.
The app now records what Enter or Escape went down on: the open dialog,
the command search, Edit Toolbar's drawer or Presentation Mode. When that
changes, the key's presses are removed from the input until it is let go.
The tool keys, the dialog's OK and the dialog's Escape also ignore
repeats, so holding Escape with the place cursor loaded discards one file.
A new press still acts, and Enter held in the Type tool still repeats line
breaks.
This also fixes these related faults:
- Ctrl+Enter or Cmd+Enter opened Move and confirmed it in the same frame.
- Holding the Enter that confirmed a dialog typed line breaks into the
text being edited.
- A command bound to Enter reopened its dialog after the dialog's OK.
- Holding Enter on a value a field rejected confirmed the dialog.
- A command search that opened while Enter was down ran its first result.
- Holding Escape over a dialog's Tips closed the dialog as well.
- Holding Escape over a dialog went on to close Edit Toolbar's drawer,
leave isolation mode or run a command bound to Escape.
The check tested one box per source segment for every candidate hole or
island. It now searches a tree of the segments' bounding boxes, built once
when the first contour is checked. The answer for each contour is the same,
so the output paths do not change. MAX_OFFSET_SEGMENT_PAIRS does not change.
With the Artboard tool, the canvas context menu lists the entries of the
public Use the artboard context menu page in its order, in place of the
entries for objects and the view. Lock Artboards and Unlock Artboards are
two submenus. An entry that cannot run is left out, and Undo and Redo stay
dimmed. A right-click on an artboard that is not selected selects it alone
first, and the menu measures the art for Delete Empty Artboards and Fit to
Artwork Bounds once, at the right-click.
Cut, Copy, Duplicate Artboards, Rename, Delete Artboards, Lock Selected,
Unlock Selected, Fit to Artwork Bounds, Switch Orientation and Export act on
the artboards the tool has selected, else the active one. docs/mcp.md lists
what the other entries act on.
- artboard.rename renames in one undo step and numbers several names. The
menu's Rename opens a dialog that runs it, and the Artboards panel's name
field runs it too.
- artboard.switchOrientation swaps width and height and keeps the top-left
corner, skipping square and locked artboards.
- artboard.selectAll chooses the tool and selects every artboard.
- edit.paste takes artboardsAt, and the menu's Paste centers copied
artboards on the right-click's point.
- The menu's Cut and Copy run artboard.cut and artboard.copy. In the desktop
app those commands publish to the system clipboard, and the menu shows the
Cut and Copy shortcuts for them.
- artboard.fitToArt and the panel's name field record no undo step when
nothing changes.
artboard.duplicate, copy, cut, rename and switchOrientation take their
artboards from the tool's selection when a call names none. Each now records
them in the journal as selectedArtboards, which a replay reads and a recorded
action leaves out. The copy commands also record art. switchOrientation
records scaleArt, and it takes patterns and lockedAndHidden and records what
its artboard.setProps step resolved for them.
While the Artboard tool is in use, a command chosen from any menu or the
command palette moves the tool's selection as the Artboards panel does:
artboard.new makes the new artboard active, artboard.duplicate selects the
copies, and artboard.delete and artboard.deleteEmpty make a remaining
artboard active.
The fitter accepted a piece at depth 14, or with fewer than six points,
whatever its error, so a traced outline could stay farther than the
tolerance from its points. Synthetic 2 px block noise at Paths 100 and
Corners 0 kept a piece 2.7 px off at a tolerance of 0.1.
Such a piece is now accepted only when each point is within the tolerance
of the curve. Otherwise a short run is cut at its worst point, down to
two points, and a run at the depth limit is cut in the middle, which keeps
the recursion shallow. Every other piece is unchanged. Noisy art can get
more anchors.
a_traced_pixel_that_straightens_to_no_area_... now traces at Paths 80. At
Paths 90 that pixel's no-area result came from pieces over the tolerance.
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.