Commit Graph
2855 Commits
Author SHA1 Message Date
BFlat e2d796b555 Merge pull request #2244 from dotCooCoo/fix/pdf-nested-stream-checks
Importers: PDF streams written inside arrays and dictionaries are checked before they are decoded
2026-10-16 01:56:38 +07:00
BFlat 268c2f2703 Merge pull request #2240 from dotCooCoo/fix/pdf-unparseable-color-values
PDF/.ai import: dictionary values the reader can't parse no longer crash the ink and font scans
2026-10-16 01:56:21 +07:00
BFlat c9ead98f1c Merge pull request #2133 from dotCooCoo/fix/pdf-unreadable-object-panics
Importers: a PDF dictionary value the reader can't parse no longer crashes the nesting check
2026-10-16 01:56:04 +07:00
dotCooCoo 433d78284d Importers: PDF streams written inside arrays and dictionaries are checked before they are decoded
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).
2026-10-16 01:36:23 +07:00
bflatastic cf98b7f368 PDF import: one helper for the dictionary values the reader can parse 2026-10-16 01:36:22 +07:00
dotCooCoo 9e90464f20 PDF/.ai import: dictionary values the reader can't parse no longer crash the ink and font scans
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.
2026-10-16 01:34:10 +07:00
dotCooCoo a69ddea930 Importers: a PDF dictionary value the reader can't parse no longer crashes the nesting check
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.
2026-10-16 01:34:09 +07:00
skylar taylor-barrick b12a52f724 PDF.R1: integrate guarded shared PDF writing and reading (#1894)
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.
2026-10-14 22:46:16 -06:00
skylar taylor-barrick 05f6e2e6df PDF.R1: reconcile shared PDF migration with current main
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.
2026-10-14 22:02:49 -06:00
skylar taylor-barrickandskylartaylor 0946bc5b74 ci: add comfortable runtime limits to unbounded jobs (#2077)
Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com>
2026-10-15 05:16:14 +02:00
Brandon ThomasandClaude Opus 5.5 0a33cff3f6 Release 0.11.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.11.0
2026-10-14 09:05:33 -04:00
skylar taylor-barrick d80950b3f3 CI: isolate the font cache test from parallel font loads 2026-10-14 06:40:06 -06:00
skylar taylor-barrick 664e2cec5f CI: keep command comparison fixtures independent of the clock 2026-10-14 05:10:20 -06:00
skylar taylor-barrick 2ecba1fb9c PDF.R1: reconcile shared reader with current writer 2026-10-14 03:59:03 -06:00
skylar taylor-barrick 52960faf63 PDF.W1: reconcile shared writer with current main 2026-10-14 01:23:19 -06:00
BFlat a067177778 Merge pull request #1223 from dotCooCoo/feat/shortcut-sets
Keyboard Shortcuts: Import… and File › Open read text shortcut sets (.kys)
2026-10-14 14:12:14 +07:00
bflatastic d9c9526898 Keyboard Shortcuts: shortcut set reading and matching stay bounded on hostile files
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.
2026-10-14 13:49:59 +07:00
dotCooCoo 8c889279f8 Keyboard Shortcuts: importMatches.list answers an unknown parameter key with an error
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.
2026-10-14 13:22:08 +07:00
dotCooCoo d1108bdbca i18n: Korean, Polish and Vietnamese rows for Import Shortcut Set and its commands
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".
2026-10-14 13:22:08 +07:00
dotCooCoo 04244fa2a2 i18n: remove Import Shortcut Set rows that the catalogs already have
Check Script (3374d077) added Copy Report and Not read to every catalog,
and es and fr gained Shortcut and the Imported message in a747daa3 and
9e39a6d1. The rows that Import Shortcut Set added for the same strings
became second entries, which bundled_catalogs_are_consistent rejects.
They are removed, 26 rows in cs, de, es, fr, hu, it, ja, ru, uk, zh-hans
and zh-hant, and the existing rows stay as they are.
2026-10-14 13:22:08 +07:00
dotCooCoo 612a13b39c i18n: Hungarian rows for Import Shortcut Set and its commands
Since b9716366 (#1302), Hungarian is a complete catalog: every menu
string, tl! literal and status or error message has a hu row, and the
i18n tests check it. hu.tsv gets the rows that the other complete
catalogs have for Import Shortcut Set and its commands: 35 strings, the
two plurals in the single form hu.tsv uses (a count takes the
singular), and the two error messages. Shortcut and the Imported
message already have hu rows on main, so they are not repeated. The
rows are placed after the Keyboard Shortcuts… row.
2026-10-14 13:22:08 +07:00
dotCooCoo f1e634af19 i18n: Czech rows for Import Shortcut Set and its commands
Since #1189, Czech is a complete catalog: every menu string, tl! literal
and status or error message has a cs row, and the i18n tests check it.
cs.tsv gets the rows that the other complete catalogs have for Import
Shortcut Set and its commands: 35 strings, the two plurals with one, few
and other forms, and the two error messages. Shortcut and the Imported
message already have cs rows on main, so they are not repeated. The rows
are placed after the Keyboard Shortcuts… row.
2026-10-14 13:22:08 +07:00
dotCooCoo 8c14609370 Keyboard Shortcuts: Import… and File › Open read text shortcut sets (.kys)
A shortcut set another app saved as slash-name text (a .kys file whose
text starts with a slash name and "{", such as "/Menus {") opens in
Import Shortcut Set from File › Open, from Edit › Keyboard Shortcuts ›
Import… in the desktop app, or from a drop on the window, whether or
not a document is open. The dialog proposes a target for an entry when
an earlier import kept one for that name, or else when one tool,
command or panel matches the name clearly by its id or by one of its
labels. It decodes the file's key and modifier codes from the codes
earlier imports kept, from the example in the AiCommandPalette README
(/Key 48 for the 0 key, with /Modifiers 0 taken as no modifiers), and
from VectorCraft's default shortcuts where at least two entries vote and
four votes in five agree. The user picks the remaining targets, presses
the key for each unknown key code or marks it No equivalent, and chooses
the modifiers for each unknown modifier code. Apply assigns the ready
rows with the Keyboard Shortcuts rules: a tool takes a single key,
optionally with Shift. It goes through the rows from the last one back
and leaves a row out when a later row it applies has the same shortcut
or the same tool, command or panel. Keyboard Shortcuts then shows the
result as unsaved changes until the user clicks OK. Copy Report copies
the set's name, the rows Apply leaves out and the parts of the file that
weren't read.

- shortcutSet.read is a query command: it reads a set and does not
  modify the document or the preferences. It returns each entry with
  its section, codes and line, and up to 1,000 skipped tokens, each cut
  to 80 characters. It reads at most 4 MiB of text and 20,000 entries,
  skips blocks nested more than 16 deep and cuts names at 1,024 bytes.
  With a path it reads at most 4 MiB and 4 bytes of the file, and with
  --automation-read-root (#832) the path must lie inside the read root.
  A file whose text starts with "<", such as an XML shortcut file saved
  as UTF-8, fails with "This shortcut file is in a form VectorCraft
  can't read."
- importMatches.list, importMatches.set and importMatches.reset read
  and change the matches kept with the preferences; resetting the
  preferences keeps them. With Remember My Choices on (the default),
  Apply keeps the targets and codes the user chose, and for the rows it
  assigns, it also keeps the matched targets and the codes decoded by
  agreement. A later import proposes them.
- The name matcher compares a name with ids by exact match and shared
  words. It compares the name with labels, in English and in the
  interface language, by exact match, shared words and character
  pairs. It proposes a match only when one tool, command or panel
  scores at least 0.6 and at least 0.1 more than the next one.
- When Import Shortcut Set is opened from Keyboard Shortcuts, Cancel,
  Escape and ui.dialog.cancel close it and return to Keyboard Shortcuts
  as it was (dialogs::PARENT). A document's queued Missing Fonts dialog
  (#792) opens after Keyboard Shortcuts closes.
- File › Open has a "Shortcut sets" filter, and in the desktop app
  Keyboard Shortcuts' Import… shows .json and .kys files.

Docs: docs/mcp.md, docs/control-protocol.md, docs/development.md,
docs/target-app-parity.md, docs/roadmap.md.
2026-10-14 13:22:07 +07:00
BFlat b076025a31 Merge pull request #1897 from storytold/fix/1881-typing-cmd-drag
Fix #1881: a Ctrl/Cmd drag while typing keeps what was typed
2026-10-14 12:38:12 +07:00
BFlat 3bb87989df Merge pull request #1896 from storytold/fix/pattern-tiles-brush-cms
Pattern tiles after brush edits and Color Settings; MCP docs on failed batches
2026-10-14 12:38:00 +07:00
BFlat a40e8f5aff Merge pull request #1895 from storytold/fix/1866-section-highlight
Fix #1866: Preferences and Save PDF highlight the section shown
2026-10-14 12:37:48 +07:00
BFlat 1a8e0456cb Merge pull request #1880 from marcoarnulfo/fix/tab-leader
Text: draw tab leaders in the tab's gap
2026-10-14 12:37:27 +07:00
bflatastic 7f1ad31a7b Fix #1881: a Ctrl/Cmd drag while typing keeps what was typed
With the Type tool editing, a press with Ctrl (Cmd on the Mac) lends the
Selection tool for the drag. Its drag joined the open typing session, so its
preview started from the document before the typing: the type moved or
scaled and lost what had just been typed. The typing is now kept as its own
undo step before the lent tool takes the press, as Undo already does, and
typing goes on after the drag.
2026-10-14 12:27:04 +07:00
bflatastic 6fd31d4dbd MCP docs: a failed batch leaves the files its steps wrote
The rollback of a failed command.batch (#1862) puts the documents back, not
the disk. Say so, and how to keep a batch from writing unless it succeeds.
2026-10-14 12:27:04 +07:00
bflatastic f9b051d3d1 Fix #1866: Preferences and Save PDF highlight the section shown
The section lists asked for a selectable button and then turned its frame
off while the pointer was elsewhere, which also dropped the selected row's
fill: the section shown looked like every other row. The selected row now
keeps its fill.
2026-10-14 12:27:04 +07:00
bflatastic 6bc0222074 #1723 follow-up: pattern tiles are drawn again after a brush edit or new Color Settings
A tile was kept while the pattern's art and what it shows were unchanged,
but a brush the art is stroked with lives in the document's other data, and
every tile is drawn through the color settings. A tile whose art reads more
than itself now also remembers the document's other data, and every tile
remembers the color settings it was drawn through (both weakly).
2026-10-14 12:27:04 +07:00
Marco Arnulfo eff2ccdc34 Text: draw tab leaders in the tab's gap (fixes #1877) 2026-10-14 12:27:03 +07:00
BFlat c73871eacc Merge pull request #1891 from storytold/fix/1691-shape-builder-gaps
Shape Builder: the gaps closed paths enclose are regions (#1691)
2026-10-14 12:14:45 +07:00
BFlat 2afd2654e8 Merge pull request #1859 from dotCooCoo/feat/proof-preview-prepared-document
Render: the art prepared for Separations Preview and Overprint Preview is kept between frames
2026-10-14 12:14:20 +07:00
BFlat 725c3881d4 Merge pull request #1860 from dotCooCoo/feat/web-build-clock
Web: the status bar shows measured render and UI times instead of 0.0 ms
2026-10-14 12:14:03 +07:00
BFlat aa93258ea7 Merge pull request #1861 from dotCooCoo/fix/hidden-chars-release-when-off
Type: Show Hidden Characters keeps no text nodes while it is off or no document is open
2026-10-14 12:13:42 +07:00
BFlat fe735f0e7d Merge pull request #1890 from storytold/docs/progress-1014b
Docs: ROADMAP and parity docs for the later 2026-10-14 merges (#1630–#1875); gaps noted for reporters
2026-10-14 12:13:08 +07:00
bflatastic 65e2168791 Shape Builder: the gaps closed paths enclose are regions (#1691) 2026-10-14 11:28:21 +07:00
dotCooCoo c44ece3113 Render: the art prepared for Separations Preview and Overprint Preview is kept between frames
With Separations Preview on, the renderer copied every node of the
document in every frame and separated each of its colors again. With
Overprint Preview on, it copied each object with an overprinting fill
or stroke in every frame, with the groups and layers that hold it. The
copies were new Arcs, so the geometry, stroke outline and raster
caches missed for each of them in every frame. In a release build,
drawing a 2880x1800 frame of a 50,000-path document again with the
same view and Separations Preview took 795 to 908 ms.

The renderer now keeps the art it prepared and what it was made from,
and in the next frame it uses that art again for each layer, symbol
art and member that has not changed. Only the objects that changed are
prepared again, with the layers and groups that hold them. All of the
art is prepared again when Discard White Overprint changes and, with
Separations Preview, when the plates shown, the proof setup, the
swatches, the Spot Colors option or the color settings change or a
profile is registered (cms::profiles_generation). The renderer now
draws that frame again in 98 to 107 ms, with the same pixels.

Docs: docs/target-app-parity.md.
2026-10-14 11:22:41 +07:00
dotCooCoo 014021577b Web: the status bar shows measured render and UI times instead of 0.0 ms
In the web build, now_ms() in crates/ui-egui/src/lib.rs returned 0.0.
The status bar read "render 0.0 ms · ui 0.0 ms" however long a frame
took, and the Layers panel's budget of 8 ms for the row thumbnails it
renders in a frame never ran out, so only its limit of 16 thumbnails
applied.

now_ms() now returns the milliseconds since its first call, read from
the browser's performance.now() through web-time, which the build
already has through eframe. The desktop clock is unchanged. In Chrome,
after a pan frame of 750 ms over a document of 50,000 paths, the status
bar now reads "render 750.7 ms". The Layers panel now renders the
thumbnails of 24 groups of 2,000 paths one per frame, where main
rendered nine of them in one frame of 217 ms. The canvas has no render
worker in the web build, so it still renders on the UI thread and draws
the same pixels.

Docs: docs/target-app-parity.md.
2026-10-14 11:22:41 +07:00
dotCooCoo abe89d5176 Type: Show Hidden Characters keeps no text nodes while it is off or no document is open
Show Hidden Characters keeps each text object's marks together with
the node it laid them out from. Only a frame that drew the overlay
changed what it kept. After the option was turned off, or the
document was closed, those nodes stayed alive until the overlay was
next drawn.

The canvas now takes the kept marks at the start of each frame and
puts them back only when it draws the overlay. A frame that does not
draw it, for example with the option off or with no document open,
drops the marks and their nodes. A frame that draws the overlay still
keeps the marks of the frame before.
2026-10-14 11:22:41 +07:00
bflatastic 0c0d2448da Docs: ROADMAP and parity docs for the later 2026-10-14 merges (#1630–#1875); gaps noted for reporters; macOS save sheet Format menu back to missing 2026-10-14 11:22:40 +07:00
skylar taylor-barrick e01a65d8a9 PDF.R1: integrate shared reader with bounded nesting checks 2026-10-13 21:58:01 -06:00
BFlat 5a120e323b Merge pull request #1875 from storytold/xtask/web-wasm-check
xtask: the wasm check compiles the web app; the web's SVG depth cap follows from its stack
2026-10-14 10:29:29 +07:00
BFlat 5de774835b Merge pull request #1874 from storytold/fix/krilla-no-simple-text
PDF: krilla without its text shaping, so rustybuzz and ttf-parser don't ship (#1730)
2026-10-14 10:29:19 +07:00
BFlat 1a280e1402 Merge pull request #1873 from storytold/fix/pdf-hostile-streams
Importers: PDF streams the reader would fail on are refused (#1700, #1701)
2026-10-14 10:29:09 +07:00
BFlat 8bd8e2b298 Merge pull request #1872 from storytold/fix/brushes-drawn-by-name
Importers: a brush that draws itself by name no longer crashes
2026-10-14 10:28:57 +07:00
BFlat cd1b590aa8 Merge pull request #1871 from storytold/fix/pdf-nested-containers
Importers: PDF nesting inside compressed streams no longer crashes (hayro 0.8)
2026-10-14 10:28:48 +07:00
BFlat dcd0da87cb Merge pull request #1870 from storytold/fix/svg-link-scope
Importers: an SVG's linked files are read within the automation roots again
2026-10-14 10:28:37 +07:00
bflatastic f774968b20 xtask: the wasm check compiles the web app; the web's SVG depth cap follows from its stack
`cargo xtask wasm` (part of `cargo xtask ci`) checked the wasm-safe crates but not
apps/vectorcraft-web, so web-only code was never compiled before a merge. It now runs
`cargo check --target wasm32-unknown-unknown -p vectorcraft-web` too (no Trunk, no network).

The SVG importer's cap on the web was 48, sized from native measurements. rustc links the web
build with a 1 MiB stack (`-z stack-size=1048576`; the app passes no linker arguments of its
own) and usvg uses about 15 kB a level: half the stack at that cost gives 34, set in one place
with the arithmetic.
2026-10-14 10:13:45 +07:00