The pre-scan lexed one object after every `trailer` marker over the rest of
the file, so an unterminated string or dictionary after each of many markers
made opening a crafted PDF quadratic. Each lex now gets a 4 KiB window; when
it runs out, the file is refused if `/Encrypt` appears past the window (one
search per file), so long trailers can't hide the key.
* fix(ui-egui): craft_ui::ComboBox for every combo box, so open menus grow back (egui#5225)
Repins craft-libs to aiongg/craft-libs 60d60d48 (storytold main with the shared
dropdown) and bans egui::ComboBox in clippy.toml.
* fix(ui-egui): font menu under the font field is a craft_ui combo menu that grows back
The filter row is the menu's header and only the font list scrolls, so a search
that matched nothing grows the menu back when it is cleared.
* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)
* chore(deps): craft-libs 92d392cf (#387 merged)
* chore(xtask): pdf-deps expects craft-libs 92d392cf
Pin craft-libs crates to 3723e32a, which adds the group extra-tabs hook.
The comp tabs follow the panel tabs directly, with no drag bar between
them, and the group header holds only the lock and the panel menu.
craft-libs crates move to one rev, 37c907d2 (contains main's craft-update and craft-docpath pins).
Docs: native floating windows are Windows, macOS and X11 only; the UI estimate drops the
multi-monitor panel work that is done.
Every craft-libs crate and the hayro-syntax and hayro-jbig2 patches are
pinned to storytold/craft-libs 2869b8cc. The workspace is a craft-ui
Workspace<PanelKind>: craft-ui draws the frames, tab strips, stacked
panels, floating windows and drop previews and owns the gestures.
EffectCraft keeps PanelKind, its built-in workspaces, the panel bodies,
the Timeline's comp tabs, viewer locks and the panel menu in each group
header, and the rule that no move or float empties the docked area.
window.dockAction applies craft-ui Actions; craft-ui's window.panel.*
commands, ui.dock and ui.dock.reset parse as DockCommand. Saved
workspaces are version 2 snapshots of craft-ui Workspaces; an earlier
file loads as the default layout and is left untouched.
Illustrator EPS imported as an empty layer. Three gaps in the PostScript
interpreter, each against the PostScript Language Reference:
- An operator stored under another name (`/mo /moveto load def`, as
Illustrator's prolog does for every path and paint operator) pushed the
operator's name instead of running it, so nothing was drawn.
- Device queries that return values (currentpagedevice, currentscreen,
currentflat, currenthalftone, status, dictstack, ...) pushed nothing,
shifting the stack under the procset's guards. They now push a plain
device's defaults; setscreen pops its three operands.
- `currentfile 0 (eod) /SubFileDecode filter` now skips to the end-of-data
string, so an embedded XMP packet isn't run as PostScript.
The regression test uses synthetic PostScript only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Load PDF document objects through the pinned craft-pdf parser and convert them into EffectCraft's existing drawing model with bounded traversal, recovery and retained data. Preserve generation numbers, stream limits, encrypted-document refusal and reported repairs.
Retain app-owned drawing, EPS, fonts and content/image decoding. Assert early excessive-nesting rejection without partial art, and cover tolerant arrays on native and WASM.
Validated with the22-stage local matrix and exact-head FreeBSD, Windows ARM64 and packaging checks.
Co-authored-by: Codex <noreply@openai.com>
inline_len computed (w * n * bpc).div_ceil(8) * h with Width/Height taken
straight from the content stream and no size validation — unlike decode()
and mask_image, which bound their geometry first. /W 5000000000000000000
overflows the multiply in debug and test builds; in release it wraps to a
small length and the caller slices the wrong bytes, misparsing the image
and shifting the EI resync.
Chain the arithmetic through checked_mul and return None, which the
caller already routes to the EI-scan fallback. Regression test:
inline_image_with_absurd_dimensions_falls_back_to_ei_scan — panics at
crates/pdf/src/image.rs:85 on main, passes with the chain.
mask_image read /BitsPerComponent from the attacker-controlled /SMask
dict with no validation and fed it straight into 1u32 << bpc — the main
image path rejects anything but 1|2|4|8|16 before its own identical
shift, the mask path did not. /BitsPerComponent 32 panics in debug and
test builds and wraps the shift in release, mistranslating every sample
into alpha.
Mirror decode()'s depth check: an unsupported mask depth means no usable
mask, so the image draws with the mask treated as absent. Regression
test: smask_with_absurd_bpc_is_ignored_not_fatal — panics at
crates/pdf/src/image.rs:184 on main, passes with the check.
Use checked division and remainder for EPS integer operators so i64::MIN with a divisor truncating to -1 cannot panic. Preserve the existing zero-divisor fallback and normal truncation behavior.
Add public EPS parse regressions covering extreme, fractional and ordinary operands.
PR #1223
The PDF / Illustrator import dialog now offers Composition - Retain Layer
Sizes. Each file layer's footage is cropped to its artwork bounds (fills,
stroke outlines, images, clips) and positioned to keep its place on the
page. Decoding, continuous rasterisation, shape conversion and reload share
one crop (effectcraft_pdf::layer_doc_with_sizes), so they agree.
Also collapses a match guard in the STL reader for current stable clippy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The EPS interpreter counted one operation against its budget for each
name it ran, whatever the operator did, and paths had no length limit.
Parsing one arc at a radius of 10^300 passed 3 GiB of resident memory
within 43 seconds. A loop of arc at a radius of 2.2 x 10^18 was still
running after 60 seconds, and a loop of pathbbox on a path of 100,001
elements and { {} loop } loop did not finish within 20 seconds. PDF
content had no path limit either: parsing a page that repeats
0 0 1 1 re 4,000,000 times peaked at 2.3 GiB of resident memory.
A path now holds at most 1,048,576 elements (MAX_PATH, the limit of
VectorCraft's EPS interpreter). In EPS, moveto, lineto, curveto,
closepath, arc, charpath and the rect operators check it, and an
operator that would take a path past it stops the program with
"PostScript (path limit)". In PDF, m, l, c, v, y, h and re (each adds
at most five elements) stop the content stream with
"content (path limit)" when the path already holds more than 1,048,571.
Each path element an operator makes, reads or copies (arc, rectangle
arrays, charpath, pathbbox, clip, eoclip, gsave, save) now counts
against the EPS budget. Each pass of repeat, for, loop and forall
counts too, and so does each procedure item that is not a name. A
moveto right after a moveto replaces it, as the PostScript Language
Reference describes, so text placed with moveto and show leaves one
element.
Five tests in tests_import.rs cover the loops, the path limit in both
interpreters and runs of moveto and show.