annot, compare, content, cos, export, geom, model, ocr, preflight, print and redact have no
production `unwrap`/`expect`/`panic!`/`unreachable!`/`todo!`/`unimplemented!`. The attribute
keeps it that way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`add_page` returns the crate's error when the new document has no page tree, and text pages are
filled with `if let`. The crate now denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each "checked" `expect` becomes `let … else`, `if let` or `?`, with the obvious fallback (the
dialog closes, the command is ignored, the view refresh is skipped). ui-egui and the three apps
now deny the panic lints. The Windows `build.rs` reports a failed resource step with
`cargo::error` instead of panicking.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Renderer cache: built with the entry API instead of insert-then-`expect`.
- Page-text cache: filled and read with `get`/`get_mut`.
- Schemas and form JSON: extended through `if let` instead of `as_object_mut().expect`.
- A replace-text mark of the wrong shape is an invalid-argument error.
The crate now denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The text reading order uses `let … else`, `while let` and `get_mut` instead of `expect`, and
named destinations bind their key with an or-pattern instead of `unreachable!`. The crate now
denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The outline import, bookmark lookup and page-label code use `first`/`last`, a final `Err` and a
default range instead of `expect`/`unreachable!`. The crate now denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PKCS #12 HMAC errors now propagate as `SignError`. Key-bag lookup uses `find_map` and chain
building uses `let … else`. `der::oid` (only ever given this crate's OID literals) keeps the
standard's narrow, documented `expect` exception. The crate now denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`printcraft_engine::guard` wraps `catch_unwind` and returns an error message.
- `Session::apply`: edits run on a copy of the document, so a panic leaves it unchanged and is
reported as an error. A panic while refreshing rolls the edit back.
- `Session::open`, `save_bytes`, `save_full_bytes` and `export_data` report a panic as an
open, write or export error.
- `PrintCraftApp::execute`: a command that panics shows a notice, and the app and its documents
keep running.
- The desktop app installs a panic hook that logs every panic to stderr, with a backtrace when
`RUST_BACKTRACE` is set.
Autosave and crash recovery (`RecoveryStore`) were already in place for anything that escapes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boa's parser and compiler recurse once per nesting level and have no limit of their own: about
100 nested parentheses in a field or document script overflowed a 2 MiB stack and aborted the
app, which `catch_unwind` can't stop. `printcraft_js::run` now:
- refuses a script longer than 256 KiB, or one that nests brackets more than 64 deep, chains
more than 64 prefix operators, or has more than 4096 `?`. Strings and comments are skipped.
The script fails with an error, as a syntax error would;
- runs the engine on its own thread with a 1 GiB stack, which is address space, committed only
as it's used. That covers every unbracketed construct a 256 KiB script can nest. A panic
inside the engine becomes a script error. On wasm the script still runs inline.
Regression tests: `deeply_nested_scripts_fail_instead_of_crashing`, which aborted the test
process before this change, and `ordinary_nesting_still_runs`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- js: `host()` returns a script error instead of panicking when the host state is missing.
- optimize, a11y: the graphics-state stack is read with `last`/`last_mut` instead of `expect`.
- fonts: the standard-14 ASCII map is built from `u8`, which can't fail.
- filters: zlib encoding into a `Vec` keeps the standard's narrow, documented `expect` exception.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AES blocks come from `as_chunks`, the IV from `split_first_chunk`, and the R6 hash takes its
AES key and IV with `first_chunk`, so no `expect` is needed. The crate now denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two crashes that a PDF can trigger, each with a regression test that failed before this change:
- Deeply nested arguments in a field's format script (`[[[[…` or `new Array(new Array(…`) and
deeply nested simplified field notation (`((((…` or `----…1`) recursed until the stack
overflowed, which aborts the whole app. Nesting is now capped at 64 levels; deeper input is
rejected like any other unparseable script.
- A date with a huge year (`1/1/2147483647`) overflowed `weekday`, and a `DateTime` with month 0
indexed before the month names. The weekday math is now done in i64, years beyond i32 don't
parse, and month and day names are looked up with `get`.
Also removes the crate's remaining `expect`/`unreachable!` calls and denies the panic lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI's Windows runner checks out with core.autocrlf=true, which rewrote the
signed openssl-signed.pdf fixture and made validates_a_signature_made_by_openssl
report Invalid there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stable clippy 1.99 denies chunks_exact/chunks_exact_mut with a constant
size (32 sites), which failed the test job on main and every open PR.
Behaviour unchanged; MSRV 1.90 has as_chunks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopt the shared Crafting Apps licensing standard. LICENSE-MIT names "ArtCraft Team and the
PrintCraft contributors"; LICENSE-APACHE matches the standard byte for byte; NOTICE gets the
standard header and brand paragraph above the existing third-party list. docs/brand/LICENSE-brand.txt
now holds the ArtCraft Team brand terms: the marks may be used only unmodified within PrintCraft,
and forks and modified versions must remove them. The brand entries in ATTRIBUTION.toml, AGENTS.md
§1.2 and the README follow suit, and the README's Licence section becomes the standard
"License and credits" block. xtask inherits the workspace licence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delete Pages only took the pages out of the page tree, so a bookmark, link or form widget that
referenced a deleted page kept it (and its content) in a full save. Deleted page objects are now
freed; their widgets leave the form (with fields left without widgets), and bookmarks and links
to them lose that destination.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text added with Edit ▸ Add content keeps its source string in the stream dictionary's /PCAdded.
Applying a redaction rewrote the stream's glyphs but copied the dictionary unchanged, so the
redacted text survived in the saved PDF. The redacted item now stays as plain drawn content and
is no longer editable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
version.rs (ported from PhotoCraft) called crate::cargo() and a two-argument
crate::run() that don't exist in this xtask, so xtask didn't build at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the app icon from the top of the README, matching PhotoCraft, and
show each app's icon next to its name in the Crafting Apps table in place
of the coloured name badges. The table is now identical across all seven
apps apart from the "you are here" row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>