Rust 1.95 clippy -D warnings rejects the nested if under TypedInstruction::Shading
(collapsible_match). Fold the shading lookup into the match arm guard so the Ubuntu
xtask ci gate can pass on the same toolchain Actions pins.
Co-authored-by: Cursor <cursoragent@cursor.com>
Documents are now at most vectorcraft_doc::MAX_DEPTH (32) deep, so the writer's MAX_DEPTH of 256
and its warning for art nested deeper can go; symbols and live objects nested in one another keep
their limit.
The file had an empty prolog, so a PostScript interpreter stopped at its first operator: it
couldn't be printed, placed or previewed by anything that runs PostScript. The prolog's
procedure set (VectorCraft_Legacy, written from the PostScript Language Reference and the
version 7 specification) now gives every operator the writer emits its meaning: paths and their
painting with separate fill and stroke colours and overprints, the fill rule, compound paths
painted as one path, clipping groups and masks, guides that don't print, layers that are hidden
or don't print marking nothing, and images drawn from the hex samples on the comment lines after
XI. Gradients fill with the colour set before them (the first stop's), and the type and encoding
operators take their operands and mark nothing.
VectorCraft opens a file with that prolog from its program, as before: its page has flat
gradients and no type.
Every file the writer's tests write is now run through import_with(bytes, false) and must draw
what the document it was written from draws: its paths, fills, strokes and images in the same
places and colours, with type left out (type the writer outlines is compared as its glyphs'
outlines), gradients as their first stop, layers that don't print left out and hidden objects
shown when the options write them visible. A new test prints the sample with every layer shown.
The engine's flattened-image test also opens the file as PostScript.
The page trailer the version 7 specification gives (section 3, Script Setup) is
`gsave annotatepage grestore showpage`, and annotatepage is a procedure of the app's own
prolog, which the file didn't have. The file now carries a procedure set of the writer's own
(VectorCraft_Legacy 1 0, listed in %%DocumentSuppliedResources) in a dictionary the setup begins
and the trailer ends, which defines annotatepage as marking nothing.
The Legacy .ai Options dialog gains Hidden Objects (Hidden Layer, Omit, Make Visible) and Text
(Editable, Outlines), translated in every catalog. Docs: type and hidden objects in the legacy
.ai paragraphs, the restored revision rows and roadmap row.
Drag out from a segment, corner, circle or arc to place a dimension; plain clicks pick two points or two lines. Radial dimensions on perfect arcs, picas, Away/Between and Left/Center/Right text placement. The Properties panel edits a selected dimension, with Apply to All (dimension.applyToAll) and Set as Default. Dimension Selected Paths, the Arc/R/Ø prefixes and the custom defaults are left out. The refresh returns at once in documents without dimensions (Document::dimensions); a linked dimension moved on its own keeps its place; modes, units, line types, arrows and text placement are enums; the tool has its own icon.
Type is written as version 7 text objects (§15): point type, area type and type on a path, with
fonts by PostScript name re-encoded to Windows-1252, and each run's paint, size, scaling,
tracking, leading, alignment and indents; type 8-bit text can't hold (or all, with outlineText)
is outlines. The format hides only layers, so hidden objects go to a hidden layer above their
own (hiddenObjects "layer", the default), are left out ("omit") or show ("visible"). The .ai
reader reads legacy type (To … TO, TE, %AI3_BeginEncoding) and ignores Ar; the import fuzzer
gets legacy type tokens.
A pen reports far more often than frames come (an Apple Pencil up to 240 times
a second): the canvas now hands the tool each move of the frame with its
pressure instead of the last one only, and the web shell feeds Safari's
coalesced pen events. Mouse input is unchanged.
The .ai options dialog offers Legacy (version 7). In that mode it shows Transparent Images
(Flatten, Omit) and, with more than one artboard, thumbnails to check with the file each is
saved to (long names keep their end). OK is greyed out until an artboard is checked. A save
panel's Enter no longer confirms the options dialog it opens.
aiVersion 7 saves through the legacy writer, one file per artboard; range picks the artboards
and document.save writes them again. Appearances are expanded first. Transparent images
(see-through pixels, opacity or a blending mode) are flattened with the art under them into
opaque images clipped to where they change it (transparentImages "flatten", the default), or
left out ("omit"); both warn.
export_legacy_ai writes a document as a PostScript-form .ai of version 7 (one artboard per
file), keeping every layer (name, order, Hidden, Locked) and its groups, compound paths and
clip groups. Closed paths end on their start point, so they keep their corner. The .ai reader
learns what the writer emits, and the tests read every file back.
A PDF is refused when a stream in it would decode to more than 256 MB
or has decode parameters the reader can't apply, but the check looked
only at the file's objects. A stream written directly inside an array
or a dictionary, such as the /Function of a shading, was not checked.
On main at b12a52f7, a calculator function holding 257 MiB of deflated
spaces opened after 3.0 to 4.3 s with a peak of 550 to 818 MB when it
was written in an array, in a dictionary or as a shading's function.
The same stream as an object of its own was refused in 81 ms.
hostile in pages.rs now runs unsafe_stream on every stream that the
walk of the file's objects meets, at any depth in arrays and
dictionaries, before it decodes the stream as a calculator function.
It refuses such a file with the same message. The walk is the one the
nesting check made before (64 deep at most).
Read pen and finger Pointer Events in the web shell (capture phase) instead of
eframe's touch handlers, which can't tell a pen from a finger and see every
touch twice. The pen drives the pointer with its pressure and hovers; fingers
are touch contacts for pinch, two-finger pan and undo/redo taps. Once a pen
has been seen (remembered in localStorage), a finger or a resting palm never
presses a tool on the canvas, and a pen landing cancels fingers already down.
A long press drops the tool press and opens the context menu.
The page no longer scrolls, zooms or selects under touch, and the canvas
stays inside the safe areas of a full-screen app.
Use automatic vector smooth shading without a separate control.
Enable Cap by default and keep continuous rim curves and bounded seam joins.
Integrate the shared dialog with the other three 3D effects.
The late-focus case in the original change has no reproducible path
(#1339 already covers focus on the press frame), so the button-down
defer is dropped. The real issue is the clicked_focus branch: a very
short drag inside one character produces no text selection, egui still
reports a click, and select-all on release replaced the drag. The press
position is now remembered and a release more than three points away
keeps the caret; a plain tap still selects all.
Also fixes the test placement bug: the new test sat between the #[test]
attribute and keyboard_focus_keeps_selecting_the_entire_numeric_field,
so that test no longer ran. The regression test fails on the fork-point
widgets.rs and passes with this change.
The reader's Dict::entries unwraps every value it reads, so it panics on
a value it cannot parse, such as the name /#zz or the number 12-.5
(vendor/hayro-syntax/src/object/dict.rs:104 at craft-libs ca50d63b).
Besides the nesting check in pages.rs, two loops of the import call it:
- Colors::visit (import_color.rs) looks for the file's Separation and
DeviceN spaces when a page paints in one. craft_pdf's interpret_page
catches the panic, and the import fails with "PDF page interpretation
failed internally".
- Walker::note_fonts (import_scan.rs) notes the fonts of each page. The
import does not catch that panic.
On main the nesting check in pages.rs panics on such a value first
(#2133 fixes it); with it fixed, the import reaches these two loops.
Colors::visit now reads the values with Dict::keys and get_raw, sorted
by key as before, and leaves out a value the reader cannot parse.
note_fonts reads the keys and looks each font up by its key, as before.
The shared PDF reader (craft_pdf::syntax, craft-libs ca50d63b) indexes
a dictionary by skipping its values. Its skip accepts some values that
its parse refuses, such as a name with a broken # escape (/#zz) and a
number such as 12-.5. Dict::entries unwraps the parse of each value
(vendor/hayro-syntax/src/object/dict.rs:104). The check in pages.rs
that refuses functions and color spaces built on one another without
end (nests_without_end) calls entries on every dictionary of a file
when the file opens. So document.pdfInfo, PDF import,
clipboard.importPdf and loading a .ai panicked on such a file, and
three import fuzz tests fail on main with PROPTEST_RNG_SEED=1182 and
PROPTEST_CASES=20000.
The check now reads a dictionary's values with keys and get_raw and
leaves out a value that doesn't parse.
A test in crates/pdf puts each of those values in a file with a plain
function, which opens, and in a file with a function that stitches
itself, which is still refused.
Integrate the shared PDF writer and reader as one qualified final tree. Preserve the separate writer and reader commits and review records.
All eight local CI gates and all eight hosted jobs passed; the checked source tree matches the final integration.
Preserve the writer and reader changes while integrating the current main.
The combined candidate passes all eight local CI gates and the 2,048-case
reader panic verification. Hosted checks remain required before landing.
Refs #1893, #1894.
A set whose entries sit in long, deeply nested sections kept every section's names again for each entry (up to hundreds of megabytes from a 4 MiB file); the names kept over all entries now stop at 8 MiB. Name matching compares character pairs in linear time, so a 20,000-entry set of long names is matched in about 2 s instead of 17 s.
Since #1442 (3acc29f8), a caller outside the app gets an error for a
parameter key that the command's params description doesn't name, and
every_description_parses requires each top-level entry of the params
object to start with a key. The note on query in importMatches.list had
a comma at the top level ("text in the name, target or label"), so the
description didn't parse: the test failed, and the command took any key.
The note is now in parentheses, and the command takes kind and query.
A test checks that shortcutSet.read and the three importMatches
commands take every key they read and refuse a misspelled one.
Since #1352, #1498 and #1507, Korean, Polish and Vietnamese are complete
catalogs: every menu string, tl! literal and status or error message has
a row, and the i18n tests check it. ko.tsv, pl.tsv and vi.tsv get the
rows that the other complete catalogs have for Import Shortcut Set and
its commands: the dialog's strings, the labels Import Shortcut Set…,
Read Shortcut Set File, Import Matches, Remember Import Matches and
Forget Import Matches, the two plurals and the two error messages. Copy
Report and Not read are already in all three, and Shortcut and the
Imported message in pl, so they are not repeated. The dialog's rows go
after Import Print Presets in ko and vi and after the Keyboard Shortcuts
dialog's rows in pl, and the two messages after "not a VectorCraft
shortcut set".