mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
fix/about-table-wait
532
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
48c1849142 |
Test: let the About modal settle before clicking (contributors refresh)
The 0.5.0 contributors refresh (
|
||
|
|
29b0b0c572 |
Release: check the OCR models in the app tar.gz by name (the CLI tar.gz broke the glob)
Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
|
|
0a8b230ca0 |
D1: pin and verify the pdf.js corpus (#339)
xtask corpus cloned mozilla/pdf.js at HEAD with no commit pin and no content check, and the corpus tests returned early when the directory was missing. So the corpus sweep measured a moving target, xtask/baselines/pdfjs.json was compared against whatever upstream happened to be that day, and a damaged or tampered checkout produced green results. AGENTS.md §2 asks for fixtures fetched pinned by commit and sha256-verified. Verification is two layers. The commit fixes which tree we ask for; the manifest digest fixes what landed on disk: one '<sha256> <path>' line per file, sorted by path, then one sha256 over that text. Git object ids are SHA-1, so the manifest is what makes this a sha256 check. The pin is 5d923c9 with 1455 files, which matches what the GitHub tree API reports for test/pdfs. The checkout sets core.autocrlf=false and core.eol=lf. This repository has autocrlf=true globally, so without that git would rewrite line endings on checkout and the same commit would hash differently on Windows than on Linux; the pin has to mean the same thing everywhere. A successful verification writes corpus/pdfjs/.pdfcraft-corpus.json. The corpus tests now skip only when nothing is fetched, and fail loudly when a checkout exists without that stamp. xtask check verifies before sweeping, so the baseline is never compared against an unknown tree. Checked end to end: the unrecorded-pin guard refuses to fetch; --update-pin fetches and prints the constants; appending one byte to one of the 1455 files is detected and repaired by re-fetching; removing the stamp makes the corpus tests fail with a message saying what to run. Against the verified corpus the tests pass: 964 files ok and 7 password cases. Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
c5d12ad1d7 |
View: draw page previews at the resolution they are shown (#529)
* Implemented full resolution pdf rendering * View: walk nearby print sheets without a range index Clippy rejects the preview neighbour loop because the index only selects sheets. * Forms: drop duplicated appearance redraw tests A merge copied three tests four extra times, so pdfcraft-forms failed to compile. * 529: keep previews sharp within main's texel-for-texel rule and bounded budgets Drops whole-page 2x supersampling (SUPERSAMPLE_LIMIT, whole_page_scale) and the 4096 BASE_SIDE: #260 keeps whole-page rasters texel for texel, and #419's budgets assume 2048-side backdrops. Print-preview rasters are capped to min(max_texture_side, 4096) px on a page's long side, and collected nearest sheet first within 128 MiB (the current sheet always). Co-Authored-By: Claude Opus 5.5 <[email protected]> * 529: struct-update syntax in the preview tests (clippy) --------- Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
b8b6fb2f45 |
Never crash & persist: Make Current Properties Default and page-display choices survive a restart (#340, #266) (#365)
* Never crash & persist: Make Current Properties Default and page display choices survive a restart #340: - Expose per-tool comment styles via CommentPrefs::styles(). - persist() writes a `comment_styles` array (tool, color, opacity, width, fill). - restore() reads each entry through the clamping setters; a new rgb_of helper at the read boundary refuses malformed colors whole (string, wrong length, null, non-finite) and clamps finite out-of-range values into 0..=1, so a hostile settings file can't smuggle a NaN or saturated color. #266: - The page display chosen in a document (view.layout.*, view.fit_one_page, view.fit_width_scrolling) now mirrors into view_defaults, which the existing default-layout/default-zoom keys already persist. New documents and the next launch open in the persisted display; transient zooms do not rewrite the default (matches Acrobat). Tests: - make_current_properties_default: persist + fresh app reuses the style. - the_page_display_you_choose_becomes_the_default: choice mirrors, persists, new document opens in the persisted display; Acrobat fit modes persist too. - comment_style_settings_clamp_and_refuse_hostile_values: hostile JSON is clamped or refused. cargo fmt --check and cargo clippy -p pdfcraft-ui-egui --all-targets are clean. * Fix clippy: chunks_exact(4) -> as_chunks::<4>() in signature_image.rs * 365: keep the comment-style persistence (#340) only; a saved no-fill clears a default fill The #266 half (every View choice rewriting the default page display) is dropped: Preferences already sets that default explicitly. rgb_triple moves out of the module declarations with one doc comment. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Fill & Sign dates: Italian names, since Italian is now an interface language (#494) date_languages_are_the_interface_languages requires every interface language to have date names; the unknown-language example in the session test moves from it to xx. --------- Co-authored-by: Mawfyy <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
fb46909e3f |
Bump version to 0.5.0 (from 0.4.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
|
|
49c6e2ec54 |
i18n: complete Brazilian Portuguese (pt-BR) interface catalog (#604)
* i18n: complete Brazilian Portuguese (pt-BR) interface catalog Catalog by André Cardoso (andrecards) from #288, unioned with the strings merged after the catalog was cut. Asset hash refreshed. * i18n: translate the pt-BR strings added since the #288 catalog and gate coverage * i18n: pt-br review fixes (line ending, redact/sanitize/page-boxes terminology) * i18n: credit the pt-BR translator and document full coverage Name André Cardoso (andrecards) as the pt-br catalog's translator in ATTRIBUTION.toml: the ~1,826-entry gist posted in issue #288, with the labels added since and the wording review as this change's contribution. ATTRIBUTION.md is regenerated from it. docs/localization.md moves Brazilian Portuguese from the menus-only clause into the full-coverage list, states the 1,997-entry count next to the provenance (following the Ukrainian paragraph's pattern), and lists pt-br with the languages whose coverage tests gate every command, All tools label and tl!() literal (added with the catalog). people.toml is unchanged: PR #165's translator has no entry there, and its policy is that only one's own entry may be added. * i18n: pt-br writes '>' like the other Latin catalogs (Inter has no ▸) * i18n: pt-BR final review fixes (toast grammar, contextual entries, menu gate) * pt-BR: add the labels main gained while this catalog was completed (98) and drop stale OCR keys --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
94f9ce69c6 |
i18n: add Italian (it) interface translation (#494)
* i18n: add Italian (it) interface translation - Complete it catalog (about 2,000 entries); it and it_* locales use it - Fonts test covers the Italian catalog; ▸ written as > like fr/es/ru, since Inter has no ▸ - Docs, attribution and ROADMAP Co-Authored-By: Claude Opus 5.5 <[email protected]> * it: add the labels main gained since this catalog (83), drop stale OCR keys, and coverage tests --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
a016f88a90 |
Fix non-exhaustive match in Windows signing path (#646)
* Fix non-exhaustive match in Windows signing path The PublicKey enum gained P521, BrainpoolP256, BrainpoolP384, BrainpoolP512, and Ed25519 variants, but the Windows-specific match statements in windows.rs were not updated, causing a compile error on Windows builds. Add wildcard arms that return a clean error for unsupported key types. * Windows store: a key type it can't sign with is Unsupported, not a crypto failure --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
c110c579a2 |
M4: delete pages from the Pages panel (#607)
Right-clicking a thumbnail offers Delete pages, and Delete or Backspace with the pointer over the panel deletes too, as in the organize grid: the selection, or the current page. A document keeps at least one page, so with every page picked the item is off and the keys do nothing. The selection goes and the current page moves to the page after the deleted ones; one undoable step. Parity: view.thumbnail-context-ops notes and evidence include Delete pages. Co-authored-by: William Max & Co. <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
6e13a5fc54 | Fill & Sign: remove white background from imported signature images (#637) | ||
|
|
dc4b3eee84 |
Fix #378: draw on the GPU that drives the primary display on Windows (#631)
* Fix #378: draw on the GPU that drives the primary display on Windows The low-power preference picked the AMD Ryzen integrated GPU on a desktop whose monitors all hang off an NVIDIA card. Presenting from the headless iGPU to a window on another GPU took the AMD display driver down, blacking out every monitor for two minutes before PdfCraft exited. Windows now gets the display-aware adapter choice Linux already had: EnumDisplayDevices (through the safe `winsafe` crate, MIT) says which adapter drives each desktop display and which is primary; `pick_adapter` matches that to the wgpu adapters by PCI ids. The GPU driving the primary display wins, as the built-in panel does on Linux (#8). The log names the GPU chosen, the first question when a window stays black. Synthetic tests cover the Windows device-id parser and the primary-display rule; a live test checks the enumeration on the running machine. Verified on a Ryzen 7000 + RTX 3090 desktop: the app now starts on the 3090. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01XBMGuLLMhs1h8orSxcJXER * 631: the winsafe feature comment says user (and kernel) --------- Co-authored-by: Claude Fable 5.1 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
994b42c15c |
CLI: pdfcraft-cli.exe carries a Windows version resource (#598)
pdfcraft-cli.exe had no VERSIONINFO, so Explorer's Details tab, (Get-Item pdfcraft-cli.exe).VersionInfo and inventory tools showed no version or publisher for it. A build.rs like the app's embeds one with winresource (already a build dependency of pdfcraft): the same product name and copyright, its own description and file names, and the package version. PDFCRAFT_REQUIRE_WINRES=1, which the Windows packaging already sets, makes a missing resource compiler an error; otherwise it's a warning, and other targets are unaffected. Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
74e30e5803 |
CLI: pdfcraft-cli ui refuses a control file another user could plant (#597)
* Security: pdfcraft-cli ui refuses a control file another user could plant `pdfcraft-cli ui --control FILE` sent the token and every command, typed text included, to whatever port FILE named, without checking who owned it. The README suggested `/tmp/pc.json`; in a shared /tmp another local user can create that file first, so the CLI talked to their listener. The CLI now refuses a symbolic link and anything but a regular file, a file over 64 KiB, and on Unix a file owned by another user or with any group or other permission bits (the app writes 600). The owner and mode are read from the opened file, and its device and inode must match the path's, so a swap after the check is refused too. std has no geteuid and unsafe is forbidden, so the effective uid is the owner of a file the CLI creates in the temp folder and removes. The README now uses a per-user path. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Security: stop the app when --control can't be published A control file that couldn't be written was only logged, and the app ran on with a listener nobody could reach. The failure can mean another user already created a file at a shared path to receive the commands, and a script started with --control would otherwise drive nothing without being told. The app now exits with the error, before any document opens, so no work is lost. The README and the app's usage comment say so. Co-Authored-By: Claude Opus 5.5 <[email protected]> * CLI: pdfcraft-cli ui refuses a control-file port above 65535 `ui` truncated the control file's port with `as u16`, so 65536 + N reached port N, and the token and command went to whatever listened there. A port that doesn't fit is now refused, before connecting. Co-Authored-By: Claude Opus 5.5 <[email protected]> * CLI: learn the effective uid from rustix, not a temp-file probe Review on #597: the probe made `pdfcraft-cli ui` depend on a writable temp folder to judge a control file somewhere else, created and removed a file on every call, and could leave one behind. rustix's `geteuid` is safe and can't fail, and rustix is already in Cargo.lock; it is a Unix-only dependency of the CLI, so Windows builds don't compile it. A control file of your own now works with TMPDIR pointing at a folder that doesn't exist; the probe refused it. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
4fc99e4845 |
Tab strip: shrink tabs before scrolling, keep Open in sight; ignore about:blank titles (#612)
* M3: tabs shrink to fit before they scroll, and Open stays in sight With more documents open than fit, the tabs kept their full width: they ran under the buttons on the right (#416 made them scroll) and the Open button scrolled away with them. Now the tabs shrink first, as browsers do: narrow tabs keep their width, wider ones share the rest equally and end their names in "…" (the tooltip has the whole name). Only once they are at their narrowest does the strip scroll. Open sits after the tabs, outside the scrolling area, so it is always in sight next to the controls on the right. Refs #397. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M3: a placeholder title like about:blank leaves the file name on the tab A browser's "Save as PDF" of a blank page or pop-up titles the document "about:blank" and asks viewers to show the title, so its tab and window said "about:blank". A one-word browser address (about:, blob:, data:) or a bare "Untitled" now counts as no title, and the file name is shown. A tab that does show a document title names its file on hover. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M3: arrows step through tabs that don't fit, and tabs keep most of their name Tabs shrank to 110 points before the strip scrolled, so with many open they became a few characters each and nothing showed that more were hidden. Tabs now stop at 180 points (most of a name), and once even that doesn't fit, arrows appear either side of them: each steps about one tab, and an arrow is dimmed at its end, so it is clear whether more tabs lie that way. The mouse wheel still scrolls them, and the active tab is still brought into view. New labels "Scroll tabs left" / "Scroll tabs right" are translated in every catalog (machine-written; native review welcome). Refs #397. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: William Max & Co. <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
36468f0ad4 |
Windows: a GPU that can't set up the window falls back to OpenGL (#519) (#617)
* Windows: vendor egui-wgpu 0.36.2 as published (#577) The crate as released on crates.io, plus egui's LICENSE-APACHE and LICENSE-MIT from the 0.36.2 tag (the published crate leaves them out). Not used yet: the next commit patches it and adds it to [patch.crates-io]. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Windows: a window larger than the GPU's texture limit can't close PdfCraft (#577) egui-wgpu 0.36.2 configures a window's surface at the window's own size. Beyond the device's max_texture_dimension_2d, Surface::configure fails validation and wgpu's default error handler panics. eframe configures the surface before the app is created, so no app code can intercept it (emilk/egui#8361). #578 raised the device's limit to the adapter's; this removes the crash for windows larger than even that. vendor/egui-wgpu: `winit::surface_fit` shrinks both sides by one factor so neither exceeds the limit (the longer side lands exactly on it), and egui draws at that factor into the surface. On DX12, PdfCraft's Windows default, the swapchain is stretched over the window (DXGI_SCALING_STRETCH), so the window stays complete and lined up with the pointer, only softer; wgpu's GL backend copies it unscaled into a corner instead. It applies wherever a surface is sized: install, resize (start-up restore, maximise, DPI change, fullscreen) and a recreated surface, which starts again from the window's own size. Depth and MSAA textures follow. A surface logs a warning when it becomes fitted. Why only an Outlook launch hit it (read from the code, not observed): winit creates the window at CW_USEDEFAULT, so Windows places it, and a launcher that hands ShellExecute its own window gets new windows on its monitor. winit then converts the restored logical size at that monitor's scale: 3440 points became 8600 pixels on a 250% panel. eframe's clamp compares points against the largest monitor, so it let 3440 through. Tests: surface_fit with the issue's 8600 x 3423 and edge sizes (zero, u32::MAX, a zero limit, the longer side exactly on the limit); egui-wgpu resolves to vendor/egui-wgpu and the fit stays wired into the paths that size a surface. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Windows: a GPU that can't set up the window falls back to OpenGL (#519) On a 2015 Intel GPU with no D3D12 driver, wgpu picked its GL adapter over WARP and Surface::configure failed (GpuWaitTimeout). wgpu's default error handler panics, and eframe configures the root surface before the app is created, so the OpenGL retry from #530 never ran. Vendored egui-wgpu (PdfCraft patch #519): winit::catch_errors runs a closure inside wgpu error scopes (validation, out of memory, internal) and returns the error. Painter::add_surface configures each new window's surface inside it and returns a failure as WgpuError::ConfigureSurface, so eframe returns Error::Wgpu and main retries with glow. Later reconfigures are unchanged. Tests: a real device returns a wgpu validation error from catch_errors instead of panicking (red with the scopes removed), and add_surface keeps the wiring (red with it removed). Docs: vendor/README.md patch 2, docs/development.md "Renderer fallback" and PDFCRAFT_RENDERER, ROADMAP. Co-Authored-By: Claude Opus 5.5 <[email protected]> * vendor/egui-wgpu: drop the stray .cargo-ok marker * Tests: the GPU error-scope test skips on a machine without a GPU adapter --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
f7d5ca4a07 |
Windows: a window larger than the GPU's texture limit can't close PdfCraft (#577) (#609)
* Windows: vendor egui-wgpu 0.36.2 as published (#577) The crate as released on crates.io, plus egui's LICENSE-APACHE and LICENSE-MIT from the 0.36.2 tag (the published crate leaves them out). Not used yet: the next commit patches it and adds it to [patch.crates-io]. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Windows: a window larger than the GPU's texture limit can't close PdfCraft (#577) egui-wgpu 0.36.2 configures a window's surface at the window's own size. Beyond the device's max_texture_dimension_2d, Surface::configure fails validation and wgpu's default error handler panics. eframe configures the surface before the app is created, so no app code can intercept it (emilk/egui#8361). #578 raised the device's limit to the adapter's; this removes the crash for windows larger than even that. vendor/egui-wgpu: `winit::surface_fit` shrinks both sides by one factor so neither exceeds the limit (the longer side lands exactly on it), and egui draws at that factor into the surface. On DX12, PdfCraft's Windows default, the swapchain is stretched over the window (DXGI_SCALING_STRETCH), so the window stays complete and lined up with the pointer, only softer; wgpu's GL backend copies it unscaled into a corner instead. It applies wherever a surface is sized: install, resize (start-up restore, maximise, DPI change, fullscreen) and a recreated surface, which starts again from the window's own size. Depth and MSAA textures follow. A surface logs a warning when it becomes fitted. Why only an Outlook launch hit it (read from the code, not observed): winit creates the window at CW_USEDEFAULT, so Windows places it, and a launcher that hands ShellExecute its own window gets new windows on its monitor. winit then converts the restored logical size at that monitor's scale: 3440 points became 8600 pixels on a 250% panel. eframe's clamp compares points against the largest monitor, so it let 3440 through. Tests: surface_fit with the issue's 8600 x 3423 and edge sizes (zero, u32::MAX, a zero limit, the longer side exactly on the limit); egui-wgpu resolves to vendor/egui-wgpu and the fit stays wired into the paths that size a surface. Co-Authored-By: Claude Opus 5.5 <[email protected]> * vendor/egui-wgpu: drop the stray .cargo-ok marker --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
1f0466fcae |
M2: bound image resampling buffers before allocation (#592)
Check source, destination, convolution and filter-plan sizes before allocation. Preserve original image geometry when a resize exceeds the policy, while allowing valid wide sources to shrink. Co-authored-by: OpenAI Codex <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
04f6b1a634 |
M2: bound PDF functions and reject invalid calculator operations (#581)
Limit function construction and calculator work, detect path cycles, and handle input-derived integer and stack errors through the existing optional failure path. Co-authored-by: OpenAI Codex <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
caeec49f5e |
M3: keep late startup URLs from changing the active document (#583)
Co-authored-by: Tevinhead <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
96854d8d92 |
M5: name line endings in the comment properties combo (#582)
* M5: name line endings in the comment properties combo The menu showed PDF tokens such as ROpenArrow. It now uses translated labels, and the stored /LE name is unchanged. * Fix main CI: move the rotated-field render test out of pdfcraft-forms (#591) #516 added pdfcraft-render as a dev-dependency of pdfcraft-forms for one rendering test. Both crates are layer 3, so `cargo xtask layers` (run in CI) rejects the sideways edge and main went red. The test now lives in crates/engine/tests/form_rotation.rs, where both crates are already dependencies, and forms drops the dev-dependency. The parity evidence points at the new location. Co-authored-by: Claude Opus 5.5 <[email protected]> * ja: 平ら for the Butt line ending --------- Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
22cbf65628 |
Tell the user when PdfCraft draws without a GPU (#519) (#627)
* Refs #519: tell the user when PdfCraft draws without a GPU When wgpu's adapter is a software one (WARP on Windows, llvmpipe on Linux), show a notice at launch that PdfCraft may be slow. egui-wgpu only logged it, so the #519 reporter had to find it in pdfcraft.log. The OpenGL fallback isn't covered: glow reports its renderer only through unsafe calls. The string is translated in the ten catalogs that carry the portable-mode notice, with their ATTRIBUTION.toml hashes. Co-Authored-By: Claude Opus 5.5 <[email protected]> * zh-hant: the drawing-without-a-GPU notice --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
cc31d11227 |
i18n: complete the Japanese catalog (105 missing strings) and check it like fr/de/ru/zh-hans (#587)
* i18n: complete the Japanese catalog and check it like the other languages
Japanese lacked 105 strings that other languages already have: the
open-link and open-email confirmations, DPI settings, the script sandbox
note, XFA form notes, signing, table and page-mode settings, and the
Contributors / Models statistics. Each now has a Japanese row, so the
interface no longer falls back to English for them.
`japanese_covers_ui_literals` joins the French, German, Russian and
Simplified Chinese checks: every `tl!("…")` literal in the UI source needs
a Japanese row. ATTRIBUTION.toml carries the catalog's new SHA-256.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* ja: the Fill & Sign flatten setting main gained meanwhile
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
|
||
|
|
80e6bc9d98 |
M14: complete the French (fr) catalog (#579)
Add the 47 labels and messages that had no French translation (redaction marks, undo/redo labels, file-dialog and script warnings, SHA digests, contextual Start/Subject/Edit/Type, ...). Written from the plain meaning of the English text, clean-room, following the existing fr.tsv vocabulary (vouvoiement, "biffer" for redact). Update the fr.tsv SHA-256 in ATTRIBUTION.toml and add a credit entry for the contributor in contributors/people.toml. Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
56f85f76df |
M9: more signature validation algorithms (SHA-3, RIPEMD-160, Ed25519, P-521, brainpool, PSS, rsa_sha1) (#386)
* M9: more signature validation algorithms Validation now reads what real signers write, and says when it is weak: - Digests SHA-224, SHA-512/224, SHA-512/256, SHA3-224/256/384/512 and RIPEMD-160, beside SHA-1 and SHA-2. - RSA PKCS#1 v1.5 for every digest, in `rsa_pad`: the one expected encoding is built and compared byte for byte, with or without the DigestInfo's NULL (RFC 8017 App. B.1). A signature over the bare digest, which doesn't say which hash it uses, is refused. The signature value must be below the modulus (RFC 8017 §5.2.2); s and s + n both verified before. - RSA-PSS with the hash, MGF1 hash and salt length the signature declares. - ECDSA as DER, non-minimal DER or raw r||s on P-256, P-384, P-521 and brainpoolP256r1/P384r1/P512r1 (BSI "plain" ECDSA OIDs too); brainpoolP512r1 is hand-written on crypto-bigint because no RustCrypto crate has it. - Ed25519 (RFC 8419), and the legacy adbe.x509.rsa_sha1 format. - An algorithm PdfCraft can't check is Unknown, never Invalid. - SHA-1 and RIPEMD-160 signatures validate, but the signature's details say the algorithm is weak (wording pinned by tests). - Encoding irregularities that were tolerated are listed in the details (`verify_noting`; `verify` keeps its signature). Found while testing, each with a regression test that failed before the fix: a brainpoolP512r1 public key with a coordinate >= p panicked inside crypto-bigint's `sub_mod` (the certificate comes from the PDF); it is refused now, and the point at infinity is an invalid signature, not an error. Vectors were made with OpenSSL (tests/data/README.md has the commands). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * Update comment to clarify MAC digest restrictions Co-authored-by: Copilot Autofix powered by AI <[email protected]> * M9: a legacy x509.rsa_sha1 signature whose key the verifier rejects is Invalid; only unsupported keys are "can't check yet" Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Sonnet 5.5 <[email protected]> Co-authored-by: Copilot Autofix powered by AI <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
c957288d3b |
M9: read BER signatures (DocuSign, Documenso); classify later changes narrowly (#144)
* M9: read BER signatures; later changes are classified narrowly
Real-world signatures (DocuSign, Telekom QES, Documenso) are BER: indefinite
lengths and segmented OCTET STRINGs, which the strict DER reader turned away
("DER signature wrong"). Behind that, later revisions that add DSS data or
rewrite the XMP were classified as tampering.
- cms: the CMS structure is read with `Tlv::parse_ber` (the reader #453 added
for PKCS#12); certificates and signed attributes are still verified over
their own exact bytes, signed attributes with an indefinite length are
re-encoded as DER first (RFC 5652 §5.4), and the details say the signature
was BER. High tag numbers are not read.
- pdf: validation data added after signing is a permitted change, but only
objects reached through /DSS /Certs|CRLs|OCSPs|VRI by the keys the standard
defines, with the shape of their role, and new or only grown since signing.
A /DSS entry naming other objects (a page's contents) excuses nothing.
- pdf: only the signed catalog's /Metadata is the document's XMP. A
/Type /Metadata or /Type /DSS label on any other dictionary or stream, even
one it already had when signed (a page's Form XObject), makes nothing
permitted. Negative tests at every DocMDP level.
Six of the new tests fail on the previous code: the BER signature, nested
segments, the store-growth rules, and a dictionary or stream carrying a
/Type /Metadata or /Type /DSS label. The last one let a drawn Form XObject be
rewritten under a Valid signature.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* M9: comparing a grown document security store is linear for references and bounded otherwise
only_grew compared arrays element by element, so a crafted /Certs of ~100k entries could hang
validation. Reference arrays are compared as sets; other arrays only up to 10,000 elements.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
|
||
|
|
0079bead14 |
M9: validate a document timestamp that sits in a signature field (#621)
* M9: validate a document timestamp that sits in a signature field Documenso (and others) store the document timestamp as an AcroForm signature field (`Timestamp_1`) whose /V is a /Type /DocTimeStamp dictionary. Field values only went through the signature validator, which read the RFC 3161 token as a CMS signature, compared its TSTInfo with the document digest and reported "altered or corrupted since the signature was applied" for an untouched file. - A field value with /SubFilter /ETSI.RFC3161 is validated as a timestamp. - Standalone timestamps are found with /Type /DocTimeStamp (ISO 32000-2 12.8.5) as well as the /Type /Sig older writers used; our own timestamp_document now writes /DocTimeStamp. The discovery cache (#419) selects its candidates with the same test, so it sees both. - The discovery test that lists a signature field holding a timestamp used to assert `doc_timestamp == false`, which is the bug above; it asserts true now. What that test is about (the field is listed once, not also as a standalone timestamp) is unchanged. Regression tests build the same structure with a test TSA; no third-party file is needed, and they fail without the fix. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * M9: a trusted TSA whose certificate wasn't valid when it stamped leaves a document timestamp Unknown, never Valid Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
d90b3f897f |
Refs #103: ship the OCR models in the desktop release packages (#630)
Release builds said "Text recognition isn't installed: its models are missing" because no packaging script or release job fetched the OCR models: only `cargo xtask models` does, into the source tree. - packaging/env.sh gains stage_models DEST, which runs `cargo xtask models DEST` (every ATTRIBUTION.toml kind = "model" file, SHA-256 verified, with its licence text). No model file is named, so the models PRs #433/#572 add ship without packaging changes. - `cargo xtask models` also writes ATTRIBUTION.txt crediting each model (CC-BY-SA-4.0 requires attribution when redistributed). - macOS: PdfCraft.app/Contents/Resources/models, before signing. - Windows: models\ beside pdfcraft.exe in the MSI (WiX v5 <Files> harvest) and the portable zip; test-msi.ps1 checks the folder. - Linux deb/rpm/tar.gz/AppImage, Flatpak bundle and FreeBSD tarball: share/pdfcraft/models beside bin/. Models::search_dirs now also looks in <exe dir>/../share/pdfcraft/models (Models::dirs_beside_exe); a test builds every package layout on disk and finds the models in each. release.yml and freebsd.yml check the packages contain them. - The missing-models messages no longer tell end users to run cargo, and the 11 catalogs that keyed an old PRINTCRAFT_MODELS wording (or the previous one) now key the current English text; ATTRIBUTION hashes updated. A test checks the translations match. The web (wasm) build, which the issue reports, still has no models; this is the desktop half only. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
8b04e3cba4 |
M9: chain building checks CA constraints, key usage, path length and validity (#620)
* M9: chain building checks CA constraints, key usage, path length and validity build_chain linked certificates by name and signature only. An issuer must also be allowed to issue: basicConstraints CA:TRUE (an old v1 self-signed root without constraints still anchors), keyCertSign when there is a key usage, a pathLenConstraint that allows the CAs below it, and validity at the signing time (or the trusted timestamp / token time) when it is known. Without this, an ordinary end-entity certificate of a trusted CA, embedded in a PDF, could "issue" a certificate for any identity and chain to the trusted root. The signature details now say why an issuer was refused. Tests issue certificates in-process and cover each refusal, plus a signed PDF whose signer chains through an end-entity certificate (Unknown, not Valid). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01LXAstazhuAFyakty2m93Af * M9: unreadable keyUsage, pathLen or basicConstraints fail closed; only v1/v2 self-signed certificates count as old roots - a present keyUsage that doesn't parse allows no usage (it was unrestricted) - a present pathLenConstraint that doesn't parse allows no CA below (it was unlimited) - basicConstraints counts as present before it is parsed, so a garbled one can't make a certificate look like an old root - the self-signed-without-basicConstraints exception applies to v1/v2 certificates only - restores the load_certificates doc comment Tests build v1 certificates when they have no extensions, as real old roots are. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M9: clippy: slice from a reference in the chain test --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
b0548e64a7 |
M14: correct Hungarian Windows Store and saved-signature labels (#595)
* M14: correct Hungarian Windows Store and saved-signature labels Follow-up to the Magyar catalog: the certificate list keeps the name Windows Store, saved signature and initials labels take the substituted noun, and the squiggly comment type names the underline. * hu: keep "Windows-tároló" — "Windows store" is the certificate store, not Microsoft's app store --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
445673fe1b |
M14: clarify Ukrainian wording and restore CI (#584)
* M14: clarify Ukrainian certificate and accessibility wording Distinguish the certificate holder from the document topic, translate fixed certificate types, and clarify fingerprints, keyboard access, table sections and equal field sizes. Validate the complete catalog with VESUM 6.8.6 and review technical-word exceptions in context. Fixes #503 Follow-up to #509 Co-authored-by: Codex <[email protected]> * M14: restore CI after merged test changes Keep one byte-identical copy of each appearance regression test instead of five definitions, preserving all three test bodies and assertions. Correct MCP resource parity evidence to the existing passing test name. Co-authored-by: Codex <[email protected]> * ROADMAP: drop the CI follow-up sentence (main fixed the duplicated tests and MCP evidence in #605/#608) --------- Co-authored-by: Codex <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
c3a717a093 |
tests: pin the interface language in the harnesses added since #502 (#593)
* tests: pin the interface language in the harnesses added since #502 #502 pinned it in every harness there was. These five arrived after its branch point, so the suite is red again on a machine whose display language has a catalog: - `commands.rs`, Clear Recent Files from the menu (#557). - `editing.rs`, `session_harness` (#462), which clicks "Don't save". - `stamps.rs`, the rotated-stamp test (#473), which clicks "OK". - `ui.rs`, removing one recent file from Home (#557), and the narrow tab strip (#416), which reads "Display theme:". The eight other `PdfCraftApp::new()` without the line never draw the shell, so `i18n::set_current` is never called and the thread-local stays on English. They compare saved state or a default, and are left alone — including the one that asserts on a `notify_tr` toast, which reads English for the same reason. Co-Authored-By: Claude Opus 5 <[email protected]> * tests: pin the interface language in the date-format and Pages-panel harnesses too --------- Co-authored-by: Fernando Areán <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
e7a15783f3 |
Export to Word/HTML/RTF: read text and images drawn by form XObjects (fixes #314) (#406)
* Export to Word/HTML/RTF: read text and images drawn by form XObjects (fixes #314) The office export built its paragraphs from text_blocks and its images from page_images. Both read only the page's own content streams (they back Edit text and Edit image, which rewrite those streams), so a page drawn through `/Fm1 Do`, as many print-to-PDF tools emit, exported as an empty document. Add read-only reading_blocks and reading_images to pdfcraft-edit. They follow Do into form XObjects (nested ones too), apply each form's /Matrix, use its own /Resources (or the caller's when it has none), and start it from the graphics state at the Do. Depth is capped at MAX_FORM_DEPTH and a form already being read is skipped, so self-referencing and mutually recursive forms are read once. export_pages uses them; editing is unchanged and still offers only the page's own text and images. * Export: a page's walk through form XObjects has a total visit budget, so forms that fan out can't hang it * Export: keep the image walk within clippy's argument limit --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
72d640f9f0 |
Fix #155: edit text across content stream pieces (#628)
A page's /Contents array is one content stream in pieces that writers may split between any two tokens (ISO 32000-2 §7.8.2). Edit text parsed each piece on its own, so an operator whose operands ended one piece and whose keyword started the next arrived with no operands: a `[(After) -20 (wards)]` / `TJ` split made the line vanish from text_lines, text_paragraphs and text_edit, and from the Edit text tool. The joined-parse logic the redaction interpreter used (#153) moves into pdfcraft-content as `Pieces`: join the pieces, parse them as one stream, map positions back to pieces, and splice joined byte ranges back into per-piece bytes (an edit replacing or removing an operator that spans pieces rewrites every piece it touches; untouched pieces keep their objects). text_lines now interprets the joined operators, and replace_line and rewrite_block splice through `Pieces`. Redaction uses the same helper. Lines and paragraphs are grouped by the piece their first operator starts in, as before, so pages whose pieces are separate drawings don't merge. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
275cbcf323 |
Refs #169: no form bar for push-button-only documents; Screen is not a comment (#615)
LaTeX `animate` PDFs store each frame as a read-only push button and drive them from a Screen annotation. PdfCraft showed "This document contains N interactive form fields." because the bar counted any field, and listed the Screen annotation in the Comments panel because only Link, Widget and Popup were filtered out. - The form-fields bar now needs at least one field that isn't a push button (something to fill in). - Non-markup annotations (ISO 32000-2 §12.5.6: Screen, Movie, RichMedia, 3D, PrinterMark, TrapNet, Watermark) are no longer comments, both when a document is inspected and when the comment list is rebuilt after an edit (new pdfcraft_annot::is_comment_subtype). Playing the animation itself (page actions, timers) is out of scope here. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
90bb2f14ad |
Refs #142: decode outline titles with BOMs and raw UTF-8 consistently (#614)
The bookmarks panel decoded /Title through lopdf::decode_text_string, which keeps a PDF 2.0 UTF-8 BOM as a visible U+FEFF, drops the whole title when the bytes after a UTF-8 BOM are not valid UTF-8, and reads BOM-less raw UTF-8 (written by some producers) as PDFDocEncoding mojibake. Outline editing in pdfcraft-organize used a second decoder, PdfString::to_text, so the two could disagree. PdfString::to_text now decodes UTF-8 after a BOM lossily, reads BOM-less strings that are valid non-ASCII UTF-8 as UTF-8, and strips stray leading U+FEFF. The inspector's text strings (titles, name-tree keys, field values) go through the same function. The reporter's file decodes correctly on main, so this is hardening of the decoder rather than a reproduction of that file. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
d483d6d475 |
Fix #423: publish a standalone Linux CLI archive (#613)
On Linux, pdfcraft-cli shipped only inside the desktop packages (about 55 MB), while macOS already publishes pdfcraft-cli-<v>-macos-universal.zip. Servers, CI and agent sandboxes only need the headless CLI. packaging/linux/package.sh gains a `cli` format, on by default, that writes pdfcraft-cli-<v>-linux-<arch>.tar.gz: the stripped pdfcraft-cli from the shared staging tree plus the licences/README via copy_docs. copy_docs now also copies NOTICE (every package benefits). The release job already uploads and checksums every file in dist/release; its package check now lists the archive and runs the extracted binary. README and docs/releasing.md list the new artifact and state the glibc 2.35 baseline. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
efb3439cb9 |
Parity: cite the MCP resources test by its merged name (#608)
#451 renamed conventions_resources_and_modern_results to conventions_resources before merging, but the misc.mcp-resources evidence kept the old name, so `cargo xtask parity` failed on main. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
f34ee9388c |
Fix main CI: drop four duplicate copies of the forms redraw tests (#605)
Four squash merges in a row (#450, #414, #451, #351) each re-added the same 206-line block from #417 to crates/forms/src/tests.rs, so the crate's tests no longer compiled (E0428: three test functions defined five times) and main went red on every platform. Keep the first copy. The same merges repeated one ROADMAP log line four times; keep one. Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
+1 |
cecbfad0ae |
D2, D3: fuzz every tool's arguments, and prove root confinement (#351)
* XFA: choice lists, signature, password, image and barcode fields; PNG and GIF pictures The xfa crate lays out the field kinds that were left blank or shown as text fields: - Choice lists become combo boxes (editable with textEntry) or list boxes (open="always"; multi-select with open="multiSelect"), with the shown items and the saved values of <items save="1"> as /Opt pairs, /V, /I and /DV as saved values, several on separate lines in the data. Field::saved_value maps shown text to saved values for the layout and the script tree alike, so scripts read what is saved. - Password fields are password text fields; signature fields are /Sig fields the app can sign. - Image fields show the picture the data (base64) or the template holds; barcode fields show their value as text, with a warning that no bars are drawn. - PNG and GIF pictures (fields and draws) are decoded to Flate image XObjects with a soft mask for transparency; JPEGs stay as they were. Sizes come from the headers, so layout never decodes; decoding is capped at 40 million pixels and failures are warnings, not errors. Engine: multi-select list values from scripts and data are one per line. README and parity notes updated (form.xfa-dynamic). Hardened after an adversarial review pass: picture sizes refused from the header past 20 million pixels or 65,535 a side, the GIF decoder given limits (a frame far larger than its screen is refused before allocation), template pictures read whole up to 32 MB, pictures decoded once and remembered in the catalog by content hash so re-layouts reuse them, data pictures that aren't images reported by field; choice values matched against saved values first, several values deduplicated and capped, /I ascending and unique, /DV through the same mapping, <value> children read, the item cap reported; the reopen path takes another viewer's choice values (several per line); CMYK JPEGs inverted only with Adobe APP14. * D5: README for cos and engine, and an honest cos status line Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets. Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes. Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true. Co-Authored-By: Claude Opus 5 <[email protected]> * D2, D3: fuzz every tool's arguments, and prove root confinement AGENTS.md §3 and §4 treat tool arguments as untrusted, but nothing fed hostile arguments to the tool table: xtask fuzz mutates PDFs only. And tools::check_args validates shape alone (object, no unknown keys, required present), not types or ranges, so a string where an integer belongs or i64::MIN where a page number belongs reaches the handler. Two tests. The first calls every tool with a zoo of wrong types, numeric extremes, empty and 100,000-character strings, NUL and right-to-left-override names, mistyped and deeply nested containers: 19,299 calls over 132 tools, each required to return Ok or a readable ToolError rather than panic. The second is the D3 audit mechanized: every path argument is tried with .. traversal, absolute, drive and UNC paths, and a planted symbolic link, and must be refused; 451 attempts, all refused. Reading the code says every handler resolves its own paths and that no tool dispatches another internally, so the PhotoCraft hole does not exist here. A test keeps that true as tools are added; a written finding would not. Both were proved to fail before passing. An out-of-bounds index in page_rotate is reported as: page_rotate panicked on {"degrees":-1,"doc":1}. Disabling resolve() is reported as 82 leaks naming the tool and path, including doc_save writing through ../ and through the symbolic link. The first draft of the confinement test did not catch a fully disabled resolve(): almost every tool failed on a non-existent document id before it ever looked at a path, and a file-listing diff cannot see a read. It now opens a real document first so handlers reach their path arguments, and asserts each call is refused rather than only diffing the filesystem. Co-Authored-By: Claude Opus 5 <[email protected]> * Preserve alternate appearances and indirect push-button values Keep rollover, down and custom appearance entries when normal appearances are regenerated, without modifying shared dictionaries. Resolve indirect button caption and layout values. Cover full and incremental saved output, and wait for raster readiness in the existing Linux scroll test. Co-authored-by: Codex <[email protected]> * M13: common MCP tools through the confined automation path Preserve task tools and compact mode, add complete hints and strict argument-key errors, and catch escaped tool panics. Co-Authored-By: OpenAI Codex <[email protected]> * M13: session resources and version-aware MCP result hints Keep per-document resources and legacy response shapes; add document/command resources and private cache hints for modern clients. Co-Authored-By: OpenAI Codex <[email protected]> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <[email protected]> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> * engine README: the execution plan is local-only, so don't link a path that doesn't exist * Merge land/450 into land/414; XFA pictures are found again by a SHA-256 on their XObject The image cache was a /PCXfaImages map in the catalog, keyed by DefaultHasher (not stable across Rust releases) and read back from the file. Each image XObject now records the SHA-256 of its picture's bytes, and a re-layout finds the XObjects on the pages it replaces by that hash; nothing goes in the catalog. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Merge land/414 into land/451; keep main's MCP protocol versions, cap command_batch at 1000 steps The 2026-07-28 revision and its resultType/ttlMs/cacheScope fields are dropped: main doesn't advertise them and they couldn't be checked against the spec. command_batch refuses more than 1000 steps. docs/mcp.md says that unknown argument keys are now refused with -32602 (a behaviour change for clients that sent extra keys). Co-Authored-By: Claude Opus 5.5 <[email protected]> * Merge land/451 into land/351; escape targets stay in the test's temp dir, and out/image paths are tried Absolute escape targets are now paths under the test's own temp base instead of /etc/passwd, C:\Windows\… and a UNC share, so a confinement bug can only touch files the test made. String 'out' and 'image' properties (and any described as a file path) are tried too. The backslash traversal runs only on Windows: elsewhere it is an ordinary file name inside the root. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Yuvraj Singh <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: sanyamsingh001 <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: storybored379 <[email protected]> Co-authored-by: OpenAI Codex <[email protected]> Co-authored-by: Flowershangfromthebranches <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
+2 |
db1b3d065d |
MCP: common command tools, session resources and client hints (#451)
* XFA: choice lists, signature, password, image and barcode fields; PNG and GIF pictures The xfa crate lays out the field kinds that were left blank or shown as text fields: - Choice lists become combo boxes (editable with textEntry) or list boxes (open="always"; multi-select with open="multiSelect"), with the shown items and the saved values of <items save="1"> as /Opt pairs, /V, /I and /DV as saved values, several on separate lines in the data. Field::saved_value maps shown text to saved values for the layout and the script tree alike, so scripts read what is saved. - Password fields are password text fields; signature fields are /Sig fields the app can sign. - Image fields show the picture the data (base64) or the template holds; barcode fields show their value as text, with a warning that no bars are drawn. - PNG and GIF pictures (fields and draws) are decoded to Flate image XObjects with a soft mask for transparency; JPEGs stay as they were. Sizes come from the headers, so layout never decodes; decoding is capped at 40 million pixels and failures are warnings, not errors. Engine: multi-select list values from scripts and data are one per line. README and parity notes updated (form.xfa-dynamic). Hardened after an adversarial review pass: picture sizes refused from the header past 20 million pixels or 65,535 a side, the GIF decoder given limits (a frame far larger than its screen is refused before allocation), template pictures read whole up to 32 MB, pictures decoded once and remembered in the catalog by content hash so re-layouts reuse them, data pictures that aren't images reported by field; choice values matched against saved values first, several values deduplicated and capped, /I ascending and unique, /DV through the same mapping, <value> children read, the item cap reported; the reopen path takes another viewer's choice values (several per line); CMYK JPEGs inverted only with Adobe APP14. * D5: README for cos and engine, and an honest cos status line Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets. Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes. Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true. Co-Authored-By: Claude Opus 5 <[email protected]> * Preserve alternate appearances and indirect push-button values Keep rollover, down and custom appearance entries when normal appearances are regenerated, without modifying shared dictionaries. Resolve indirect button caption and layout values. Cover full and incremental saved output, and wait for raster readiness in the existing Linux scroll test. Co-authored-by: Codex <[email protected]> * M13: common MCP tools through the confined automation path Preserve task tools and compact mode, add complete hints and strict argument-key errors, and catch escaped tool panics. Co-Authored-By: OpenAI Codex <[email protected]> * M13: session resources and version-aware MCP result hints Keep per-document resources and legacy response shapes; add document/command resources and private cache hints for modern clients. Co-Authored-By: OpenAI Codex <[email protected]> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <[email protected]> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> * engine README: the execution plan is local-only, so don't link a path that doesn't exist * Merge land/450 into land/414; XFA pictures are found again by a SHA-256 on their XObject The image cache was a /PCXfaImages map in the catalog, keyed by DefaultHasher (not stable across Rust releases) and read back from the file. Each image XObject now records the SHA-256 of its picture's bytes, and a re-layout finds the XObjects on the pages it replaces by that hash; nothing goes in the catalog. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Merge land/414 into land/451; keep main's MCP protocol versions, cap command_batch at 1000 steps The 2026-07-28 revision and its resultType/ttlMs/cacheScope fields are dropped: main doesn't advertise them and they couldn't be checked against the spec. command_batch refuses more than 1000 steps. docs/mcp.md says that unknown argument keys are now refused with -32602 (a behaviour change for clients that sent extra keys). Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Yuvraj Singh <[email protected]> Co-authored-by: Tanish Manku <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: sanyamsingh001 <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: storybored379 <[email protected]> Co-authored-by: OpenAI Codex <[email protected]> Co-authored-by: Flowershangfromthebranches <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
1ba8b194b2 |
XFA: choice lists, signature, password, image and barcode fields; PNG and GIF pictures (#414)
* XFA: choice lists, signature, password, image and barcode fields; PNG and GIF pictures The xfa crate lays out the field kinds that were left blank or shown as text fields: - Choice lists become combo boxes (editable with textEntry) or list boxes (open="always"; multi-select with open="multiSelect"), with the shown items and the saved values of <items save="1"> as /Opt pairs, /V, /I and /DV as saved values, several on separate lines in the data. Field::saved_value maps shown text to saved values for the layout and the script tree alike, so scripts read what is saved. - Password fields are password text fields; signature fields are /Sig fields the app can sign. - Image fields show the picture the data (base64) or the template holds; barcode fields show their value as text, with a warning that no bars are drawn. - PNG and GIF pictures (fields and draws) are decoded to Flate image XObjects with a soft mask for transparency; JPEGs stay as they were. Sizes come from the headers, so layout never decodes; decoding is capped at 40 million pixels and failures are warnings, not errors. Engine: multi-select list values from scripts and data are one per line. README and parity notes updated (form.xfa-dynamic). Hardened after an adversarial review pass: picture sizes refused from the header past 20 million pixels or 65,535 a side, the GIF decoder given limits (a frame far larger than its screen is refused before allocation), template pictures read whole up to 32 MB, pictures decoded once and remembered in the catalog by content hash so re-layouts reuse them, data pictures that aren't images reported by field; choice values matched against saved values first, several values deduplicated and capped, /I ascending and unique, /DV through the same mapping, <value> children read, the item cap reported; the reopen path takes another viewer's choice values (several per line); CMYK JPEGs inverted only with Adobe APP14. * D5: README for cos and engine, and an honest cos status line Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets. Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes. Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true. Co-Authored-By: Claude Opus 5 <[email protected]> * Preserve alternate appearances and indirect push-button values Keep rollover, down and custom appearance entries when normal appearances are regenerated, without modifying shared dictionaries. Resolve indirect button caption and layout values. Cover full and incremental saved output, and wait for raster readiness in the existing Linux scroll test. Co-authored-by: Codex <[email protected]> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <[email protected]> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> * engine README: the execution plan is local-only, so don't link a path that doesn't exist * Merge land/450 into land/414; XFA pictures are found again by a SHA-256 on their XObject The image cache was a /PCXfaImages map in the catalog, keyed by DefaultHasher (not stable across Rust releases) and read back from the file. Each image XObject now records the SHA-256 of its picture's bytes, and a re-layout finds the XObjects on the pages it replaces by that hash; nothing goes in the catalog. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Tanish Manku <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: sanyamsingh001 <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Flowershangfromthebranches <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
37076662a7 |
M7: select and edit grouped PDF figures (Form XObjects) (#450)
* D5: README for cos and engine, and an honest cos status line Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets. Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes. Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true. Co-Authored-By: Claude Opus 5 <[email protected]> * Preserve alternate appearances and indirect push-button values Keep rollover, down and custom appearance entries when normal appearances are regenerated, without modifying shared dictionaries. Resolve indirect button caption and layout values. Cover full and incremental saved output, and wait for raster readiness in the existing Linux scroll test. Co-authored-by: Codex <[email protected]> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <[email protected]> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> * engine README: the execution plan is local-only, so don't link a path that doesn't exist --------- Co-authored-by: Tanish Manku <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: sanyamsingh001 <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
9a529a4fbe |
Preserve alternate appearances and indirect push-button values on redraw (#417)
* D5: README for cos and engine, and an honest cos status line Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets. Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes. Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true. Co-Authored-By: Claude Opus 5 <[email protected]> * Preserve alternate appearances and indirect push-button values Keep rollover, down and custom appearance entries when normal appearances are regenerated, without modifying shared dictionaries. Resolve indirect button caption and layout values. Cover full and incremental saved output, and wait for raster readiness in the existing Linux scroll test. Co-authored-by: Codex <[email protected]> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> * engine README: the execution plan is local-only, so don't link a path that doesn't exist --------- Co-authored-by: Tanish Manku <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: sanyamsingh001 <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
2d40d85450 |
D5: READMEs for cos and engine (#332)
* D5: README for cos and engine, and an honest cos status line Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets. Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes. Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true. Co-Authored-By: Claude Opus 5 <[email protected]> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> * engine README: the execution plan is local-only, so don't link a path that doesn't exist --------- Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: Streetblock <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
025b79b515 |
M14: honor Windows display language with neutral launcher locales (#567)
* M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <[email protected]> * pdfcraft-cli --help lists `mcp` only when the build has it Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Codex <[email protected]> Co-authored-by: lloyd <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
fce60eacb4 |
pdfcraft-cli --help lists mcp only when the build has it (#574)
Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed `pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command". USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead of calling it an unknown command. - tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and mcp_without_the_feature_says_it_was_left_out (without the feature) Co-authored-by: lloyd <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
dbd0748177 |
Reduce large-PDF memory growth (#307) (#419)
* M14: bound large-PDF memory use (#307) Inspect large documents lazily, share parser/resource metadata, bound full-save and raster retention, and cache signature candidate discovery safely. Preserve upstream rendering deduplication, pixel alignment, signature previews, and organizer insertion behavior, with regression coverage for their interaction. Validated with 1,336 workspace tests, 193 parser tests, strict workspace Clippy, 30 WASM crate checks, 1x/2x GUI protocols, and same-PDF workflow comparisons. * cos: drop the writer cache-policy probe and its test-only hooks in production paths * cos: the temporary reader keeps parsed objects until a count or owned-bytes budget Since #552 a stream read from the file is a view into the input, so clearing the whole parse cache after every read saved little memory and cost most of the full-save slowdown (every object parsed twice, indirect /Length objects re-parsed per stream). Clear only past OBJECTS entries or STREAM_BYTES of data of the cache's own (decrypted streams, strings). * cos: the writers refuse a document opened with a stream limit (read-only inspection) * cos: a full save fails rather than writing null when an object reads differently the second time * render: parsing is not timed by the watchdog; timing tests with wide margins A slow first parse (a huge or damaged file) made the whole pool fail every page for its lifetime. Parsing, shared or private, is now its own untimed stage, as on main before the shared parser; rendering is timed from when parsing ends. The init-timeout tests become one deterministic test (a 2 s parse against a 10 ms limit, shared and private). Watchdog tests use limits and injected delays ~10x apart instead of 25-50 ms against real renders. Also: saturating retired-worker count, main's stop-flag test runs on private parsers (the first shared timeout is a private retry), set_queue documents that unlisted results are dropped, and retire() documents the permanent per-worker-parser fallback. * render: page numbers in the lazy page tree use checked conversion * render: the lazy inspector is compared with lopdf on named destinations and object streams too * ui: on-screen pages always render; pages turned back to stay cached; stable thumbnail tags - Visible pages and tiles are always admitted and uploaded (the viewport bounds them); the byte and count limits decide only how much prefetch fits. A HiDPI two-up view or a 5K viewport left pages on 'Rendering...' for good. - Pages no longer demanded stay cached, nearest the current page first, while the 128 MiB allowance and the 24-page count have room: paging back no longer re-renders. - Thumbnail density follows the demand count rounded up to a power of two, so rows scrolling in and out of an organize grid don't change every thumbnail's tag and re-render it. - A suspended (background) tab drains its pool's finished results instead of holding them. - Organize drag-and-drop ends on the primary button only. - Tests: the stale-result test uses valid pixels and checks nothing was uploaded; the organizer wheel test runs 40 steps; regressions for the first three fixes. * vendor: mark the hayro-syntax lock and SegmentList patches and list them as (8) and (9); clippy 1.99 as_chunks in a raster test * render: clippy 1.99 — contains over iter().any() in a raster test --------- Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
75b31fb04d |
M10: Print ▸ Properties… opens the printer driver's own settings (#482)
* M5.7: date format and date language for Fill & Sign in the engine * M5: flatten Fill & Sign marks when saving Saving can now bake typed text, marks and signatures into the page, from Preferences or doc_save. The option defaults off, so a normal save still leaves them editable, and other comments and form fields stay. * M4: zoom the Organize page grid The page grid draws its pages at 50% to 300%: toolbar buttons with a percentage that resets to 100%, trackpad pinch or Ctrl/⌘ + wheel about the page under the pointer, and ⌘+ / ⌘− / ⌘0 (which no longer change the hidden page view's zoom while the grid shows). Thumbnails are re-rendered sharper in a few steps as the grid grows, capped so one document's thumbnails stay within 256 MB. The page under the pointer (or the first one in view) stays where it is across a zoom. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M4: zoomed grid pages are rendered at the size drawn A zoomed page grid stretched 132 pt thumbnails, and the stepped detail that was meant to help was capped by a memory budget, so a long document stayed blurry at any zoom. Pages drawn larger than their thumbnails now get a render at exactly the size drawn while they are in view; the thumbnail stands in until it arrives. Only pages in view keep one, so memory no longer depends on the page count, and thumbnails go back to their one fixed size. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M5.7: fill_sign_date_format tool; format and language on fill_sign_add * M5.7: Preferences ▸ Date format for Fill & Sign dates (refs #158) * M5.7: refuse Fill & Sign dates the PDF text font can't hold * M5.7: Spanish labels for Preferences ▸ Date format * M10: Print ▸ Properties… opens the printer driver's own settings CUPS: the queue's PPD job options (with lpoptions defaults), grouped in the Print dialog and sent with this job as lp -o key=value; installable options and what the dialog sets itself are left out. Windows: the driver's Printing Preferences window through printui (no unsafe FFI), saved as the printer's per-user defaults. Headless: printer_options tool and doc_print options. Co-Authored-By: Claude Opus 5.5 <[email protected]> * View: the middle button pans instead of drawing with markup tools egui's drag responses accept every pointer button, so on Windows and macOS a middle-button drag reached the page tools: with Draw (ink) selected it drew a stroke, and other tools cropped, selected or moved. Linux was unaffected because its middle-click auto-scroll already blocks page input. Outside Linux a middle-button drag that starts in the viewport (document or Organize Pages) now pans 1:1 while the button is held, with a Grabbing cursor, and page input is blocked from the press through the release. Linux keeps its latched auto-scroll. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M3: a short page gone to from the Pages panel stays current The current page was whichever page showed most of itself. Going to a short page (a cheque) above a tall one scrolls it to the top, where the tall page below shows more, so the current page and the Pages panel's highlight moved to the tall page instead of the one clicked. The page navigated to now stays current while it is wholly on screen. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M4: page operations from the Pages panel: context menu and drag to reorder Right-clicking a thumbnail offers Extract pages, Cut, Copy and Paste after, on the selection or on the page clicked (which becomes the selection), as the organize grid does. Dragging thumbnails moves the selection, or the page grabbed, to the gap it is dropped on: a bar marks the gap, the page count follows the pointer, and the panel scrolls near its edges. The move is one undoable step; the moved pages stay selected and the current page follows its page. Documents that forbid assembly keep click-only thumbnails. Parity: view.thumbnail-context-ops is shipped, with what it still lacks. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Pages panel: only the primary button drags thumbnails; a drag left over after the panel closed or the document changed is dropped * Fill & Sign dates: German, Bulgarian, Hungarian and Ukrainian month and weekday names, matching the interface languages * uk: keep the author's wording for the flatten setting --------- Co-authored-by: KissT <[email protected]> Co-authored-by: Mark Bartalos <[email protected]> Co-authored-by: samehelhawary <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Cody Vogt <[email protected]> Co-authored-by: William Max & Co. <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |
||
|
|
d592dbb9c4 |
Fill & Sign: date format and date language in Preferences (refs #158) (#505)
* M5.7: date format and date language for Fill & Sign in the engine * M5: flatten Fill & Sign marks when saving Saving can now bake typed text, marks and signatures into the page, from Preferences or doc_save. The option defaults off, so a normal save still leaves them editable, and other comments and form fields stay. * M4: zoom the Organize page grid The page grid draws its pages at 50% to 300%: toolbar buttons with a percentage that resets to 100%, trackpad pinch or Ctrl/⌘ + wheel about the page under the pointer, and ⌘+ / ⌘− / ⌘0 (which no longer change the hidden page view's zoom while the grid shows). Thumbnails are re-rendered sharper in a few steps as the grid grows, capped so one document's thumbnails stay within 256 MB. The page under the pointer (or the first one in view) stays where it is across a zoom. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M4: zoomed grid pages are rendered at the size drawn A zoomed page grid stretched 132 pt thumbnails, and the stepped detail that was meant to help was capped by a memory budget, so a long document stayed blurry at any zoom. Pages drawn larger than their thumbnails now get a render at exactly the size drawn while they are in view; the thumbnail stands in until it arrives. Only pages in view keep one, so memory no longer depends on the page count, and thumbnails go back to their one fixed size. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M5.7: fill_sign_date_format tool; format and language on fill_sign_add * M5.7: Preferences ▸ Date format for Fill & Sign dates (refs #158) * M5.7: refuse Fill & Sign dates the PDF text font can't hold * M5.7: Spanish labels for Preferences ▸ Date format * View: the middle button pans instead of drawing with markup tools egui's drag responses accept every pointer button, so on Windows and macOS a middle-button drag reached the page tools: with Draw (ink) selected it drew a stroke, and other tools cropped, selected or moved. Linux was unaffected because its middle-click auto-scroll already blocks page input. Outside Linux a middle-button drag that starts in the viewport (document or Organize Pages) now pans 1:1 while the button is held, with a Grabbing cursor, and page input is blocked from the press through the release. Linux keeps its latched auto-scroll. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M3: a short page gone to from the Pages panel stays current The current page was whichever page showed most of itself. Going to a short page (a cheque) above a tall one scrolls it to the top, where the tall page below shows more, so the current page and the Pages panel's highlight moved to the tall page instead of the one clicked. The page navigated to now stays current while it is wholly on screen. Co-Authored-By: Claude Opus 5.5 <[email protected]> * M4: page operations from the Pages panel: context menu and drag to reorder Right-clicking a thumbnail offers Extract pages, Cut, Copy and Paste after, on the selection or on the page clicked (which becomes the selection), as the organize grid does. Dragging thumbnails moves the selection, or the page grabbed, to the gap it is dropped on: a bar marks the gap, the page count follows the pointer, and the panel scrolls near its edges. The move is one undoable step; the moved pages stay selected and the current page follows its page. Documents that forbid assembly keep click-only thumbnails. Parity: view.thumbnail-context-ops is shipped, with what it still lacks. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Pages panel: only the primary button drags thumbnails; a drag left over after the panel closed or the document changed is dropped * Fill & Sign dates: German, Bulgarian, Hungarian and Ukrainian month and weekday names, matching the interface languages --------- Co-authored-by: Mark Bartalos <[email protected]> Co-authored-by: samehelhawary <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Cody Vogt <[email protected]> Co-authored-by: William Max & Co. <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> |