mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 12:38:58 -06:00
fix/forms-duplicate-tests
200
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
+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 <noreply@anthropic.com> * 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 <codex@openai.com> * 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 <noreply@openai.com> * 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 <noreply@openai.com> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <noreply@openai.com> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <codex@openai.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Yuvraj Singh <singh.yuvraj31@icloud.com> Co-authored-by: Tanish Manku <tmanku.dev@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com> Co-authored-by: storybored379 <storybored379@users.noreply.github.com> Co-authored-by: OpenAI Codex <noreply@openai.com> Co-authored-by: Flowershangfromthebranches <152056395+Flowershangfromthebranches@users.noreply.github.com> Co-authored-by: Streetblock <github@streetblock.de> Co-authored-by: lloyd <lloyd@stackworx.io> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
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 <noreply@anthropic.com> * 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 <codex@openai.com> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <noreply@openai.com> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <codex@openai.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Tanish Manku <tmanku.dev@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com> Co-authored-by: Flowershangfromthebranches <152056395+Flowershangfromthebranches@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Streetblock <github@streetblock.de> Co-authored-by: lloyd <lloyd@stackworx.io> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
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 <noreply@anthropic.com> * 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 <codex@openai.com> * M7: select and edit grouped Form XObject figures Co-Authored-By: Codex <noreply@openai.com> * M14: let neutral Windows locales fall through to display language Co-authored-by: Codex <codex@openai.com> * 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 <noreply@anthropic.com> * engine README: the execution plan is local-only, so don't link a path that doesn't exist --------- Co-authored-by: Tanish Manku <tmanku.dev@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Streetblock <github@streetblock.de> Co-authored-by: lloyd <lloyd@stackworx.io> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
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 <codex@openai.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: lloyd <lloyd@stackworx.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <github@kisst.net> Co-authored-by: Mark Bartalos <bartalos86@gmail.com> Co-authored-by: samehelhawary <basio20044@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cody Vogt <Cody.Vogt@caltechsurveys.com> Co-authored-by: William Max & Co. <info@williammax.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <bartalos86@gmail.com> Co-authored-by: samehelhawary <basio20044@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cody Vogt <Cody.Vogt@caltechsurveys.com> Co-authored-by: William Max & Co. <info@williammax.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
8e3782d5ca |
Flatten Fill & Sign marks when saving (#464)
* 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * Pages panel: only the primary button drags thumbnails; a drag left over after the panel closed or the document changed is dropped * M5: translate Fill & Sign flatten labels into Ukrainian The coverage test failed because the new preference strings had no Ukrainian entries. * 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 <noreply@anthropic.com> * uk: the author's wording for the flatten setting --------- Co-authored-by: samehelhawary <basio20044@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cody Vogt <Cody.Vogt@caltechsurveys.com> Co-authored-by: William Max & Co. <info@williammax.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
f28cbbcdd9 |
M4: zoom the Organize page grid (#468)
* 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * Pages panel: only the primary button drags thumbnails; a drag left over after the panel closed or the document changed is dropped --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cody Vogt <Cody.Vogt@caltechsurveys.com> Co-authored-by: William Max & Co. <info@williammax.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
d1b7e98698 |
Pages panel: keep a short page current; context menu and drag to reorder (#573)
* 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * Pages panel: only the primary button drags thumbnails; a drag left over after the panel closed or the document changed is dropped --------- Co-authored-by: Cody Vogt <Cody.Vogt@caltechsurveys.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: William Max & Co. <info@williammax.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
a48e1a3ffe |
View: the middle button pans instead of drawing with markup tools (#565)
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 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
86ec14291c |
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 <noreply@anthropic.com> |
||
|
|
3af93c7585 |
M5: draw all ten PDF line-ending styles (#520)
* M5: draw all ten PDF line-ending styles Imported lines, polylines and callouts kept Square, Circle, Diamond, Butt, Slash and reverse arrows in /LE but would not redraw them. Those styles now draw, and comment_add and Comment properties can set them. * hu, uk: Line ending --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
c9d0619226 |
Forms: rotate field widgets with /MK /R (#516)
* M6: rotate form field widgets with /MK /R Filling or restyling a rotated field redrew its text upright. Appearances now follow /MK /R, and Field Properties and form_set_props can set the rotation. * Forms: a rotation refused as too small is checked before any other property is written; no indexing by widget number * hu, uk: Rotation: --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
e6df6b1384 |
i18n: add complete Ukrainian interface translation (#509)
* M14: add complete Ukrainian interface translation Add the uk registry entry and original catalog, Ukrainian integer plurals, calendar month inflection, attribution and localization documentation. Fit Home recommendation cards and the search hint to translated text. Cover locale detection, persistence, catalogs, bundled glyphs and UI control. Fixes #503 Related to #348 Co-authored-by: Codex <codex@openai.com> * uk: add the recent-files labels, labels that reach tl!() through a variable, and 'Inserted text'; fix 'мають мати' * uk: Home / End are key-cap names --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
f626db25cc |
M14: refine German labels and prevent translated UI text overlap (#515)
* M14: refine German labels and keep translated text within UI bounds Co-authored-by: Codex <codex@openai.com> * Tool names inside notices stay lowercase in English; German keeps its capitalised nouns --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
95120862fc |
M14: Hungarian interface catalog (hu) (#545)
* M14: Hungarian interface catalog (hu) Magyar joins the interface languages as a complete catalog, so Preferences and hu locales show commands, dialogs, panels and notices in Hungarian. Wording uses informal "te" and nouns for commands; the strings were written from the meaning of the English labels. * Small grammar correction * hu: restore trailing spaces in run-on labels; add the recent-files labels and labels that reach tl!() through a variable --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4e725dcee2 |
Bookmarks and links go to their destination's position and zoom (#556)
Every destination was reduced to a page index (`dest_page` in the inspector), so a bookmark written as [p1 /XYZ 0 420 0] went to the top of page 1 instead of putting y 420 at the top of the window, and /FitH, /FitV, /FitR and an /XYZ zoom were ignored. Destinations now keep their view (ISO 32000-2 §12.3.2.2): - render: `DestView` (Top, Xyz, Fit, FitH, FitV, FitR) parsed from explicit arrays, /D dictionaries and named destinations (/Names /Dests and /Dests). `OutlineItem` gains `view`; `LinkTarget::Page` carries it. Missing, wrong-typed or non-finite operands are null, a zoom that isn't positive keeps the zoom, and an unknown type or a /FitR without four numbers falls back to the page top. `PageInfo::dest_fraction` maps a user-space point to the displayed page through the crop box origin, /Rotate and the view rotation, clamped to the page. - ui: `DocView::go_to_dest` applies it for bookmark and link clicks (and so for `ui.click` on the control channel): /XYZ puts (left, top) at the window's top-left (beside the quick-action gutter) at its zoom; /Fit, /FitH and /FitV switch to fit page, width and height with top/left at the edge; /FitR zooms so the rectangle fits, centred. The /FitB* content box is read as the whole page. Tests: operand parsing incl. hostile values, a fixture with every form (named, GoTo, wrong types, huge numbers, no page), point mapping under crop offsets and rotations, go_to_dest state, and kittest bookmark and link clicks checking the point lands at the window's top. Co-authored-by: lloyd <lloyd@stackworx.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
19625d40a8 |
M3: a newly opened document starts at the top, not where the last session scrolled (#554)
Opening a PDF after a restart showed it part-way down, at the page some other document had been left on in the previous session. Since #259 (#189) the page view's ScrollArea is salted with the document's DocId, so each tab keeps its own offset. eframe persists egui's memory (persist_egui_memory defaults to true) to app.ron every 30 s and on quit, scroll offsets included, and restores it at the next launch. DocIds start again at 1 every launch, so the first document opened inherited the offset stored under id 1 last time, the second the one under id 2, and so on. A DocView now notes when its page view is first shown, and that frame sets the scroll offset to zero. Navigation queued before the first frame still wins: a zoom anchor or `goto` (--page, the PDF's open action, reopening the last session's files at their pages, #462) is applied on top. Tabs that were already shown keep their own position, as #189 needs. egui's other persisted memory (panel sizes and the like) is untouched. - canvas: `DocView::shown`, reset of the offset on the first frame - tests/viewer.rs: a_new_document_opens_at_the_top_after_a_relaunch saves egui's memory through eframe's own RON storage path after scrolling a.pdf to page 4, restores it in a new app, opens c.pdf (DocId 1) and checks it starts on page 1 (it opened on page 4 before the fix) and then keeps its own position - parity: the test is evidence on view.document-tabs Refs #189, #259, #462. Co-authored-by: lloyd <lloyd@stackworx.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5ddf33ccf5 |
Fix #299: typed and drawn signatures stay upright on rotated pages (#562)
On a page with /Rotate 90, 180 or 270, Fill & Sign placed typed and drawn signatures and initials sideways or upside down: their box and their strokes/outlines were laid out along user-space axes, which the page turns, while the click (or fill_sign_add's "at") is a displayed point. Only image signatures were counter-rotated (#485). Both now use the same displayed axes as image signatures (pdfcraft_model::view_matrix_for without its offset): the box's left edge and vertical centre are at the displayed point, a drawn signature's ink points are mapped through the displayed right/up vectors, and a typed signature's normalised outlines are turned back inside a user-space box laid out along the turned page (the shared signature_image::upright_box, now also used by SignatureImage::rect). The appearance is plain geometry, so every viewer shows it upright without relying on a form /Matrix. Unturned pages are unchanged (the mapping is the identity there). typed_signature_shape and fill_sign::{signature_at, typed_signature_at} take the page rotation; fill_sign::place and fill_sign_add pass it. Tests: a kittest-file test places a drawn, typed signature and typed initials on /Rotate 0/90/180/270 pages through fill_sign::place, renders them and checks each displayed ink box is wider than tall, anchored at the point, identical to the unturned page, and that an asymmetric drawn stroke is not mirrored; an automation test does the same for fill_sign_add typed signatures and initials, before and after save/reopen. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
28804d55d7 | Issue #430: clear the recent-files list, or remove one file from it (#557) | ||
|
|
9acb29e418 |
Fix #527: Shift+click extends the text selection (#560)
A plain click on a page cleared the text selection whatever modifiers were held, so there was no way to extend a selection with Shift+click as in Acrobat. Now, when Shift is held and there is a text selection on the clicked page, the click moves the selection head to the nearest text position and keeps the anchor. Without Shift, or with no selection on that page, a click still clears the selection. Selections remain single-page: cross-page selection (the issue's second request) is not part of this change. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
653f8268c2 |
Fix #273: ↓ / ↑ turn pages in single-page view (#550)
In single-page view ↓ / ↑ only added to the pending keyboard scroll, so on a page that fits the window (or once scrolled to the page's end) they did nothing, while the mouse wheel turned the page there. The page view now records, after each frame in single-page view, which page it drew and whether it was at the top and at the bottom of it. ↓ at the bottom (always, on a page that fits) steps to the next page; ↑ at the top steps to the previous page and lands on its bottom, so holding ↑ keeps scrolling back through the document. The edges only count when they belong to the current page with no jump pending, so a stale frame can't turn two pages for one press. Tested by a kittest: Fit page in single-page view, ↓ / ↑ turn one page per press; Fit width, ↓ scrolls through page 1 before turning, and ↑ from the top of page 2 returns to the bottom of page 1. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2aca0692fc |
Fix #526: keep text colour in Word, HTML and RTF export (#547)
The export model (`pdfcraft_export::Block`/`Cell`) had no colour, so `export_pages` dropped the fill colour that `edit::TextBlock` already carries and every run came out black. Block and Cell now carry an sRGB colour, mapped from TextBlock.color. Word runs get `<w:color w:val="RRGGBB"/>`, HTML paragraphs, headings and cells a `style="color:#RRGGBB"`, and RTF a colour table with `\cfN`. Black text keeps each format's default; NaN and out-of-range components clamp. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bd57377435 |
Fix #525 (item 4): phone pattern redacts international numbers whole (#543)
The Phone Numbers redaction pattern accepted only 10 digits (11 with a country code), the North American form. For "+49 2151 123456" (12 digits) the `+` candidate was rejected and the scan then matched "2151 123456", leaving "+49" visible: a partial redaction in a document the user thinks is clean. The pattern now accepts, still grouped by at least one separator: - E.164 numbers with a leading `+`: 8-15 digits in all; - national numbers with a trunk 0 followed by 6-14 digits, whose first group has at least two digits (so decimals don't match) and which are not a day-first date (left to the date pattern); - North American numbers as before. A separator may now be followed by `(`, so "+49 (0) 2151 123456" is one match instead of a partial "(0) 2151 123456". Refs #525. The other items (page-number and date tokens, stamps) are not addressed here. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
7c358099ff |
M14: German interface catalog (de) (#471)
* M14: German interface catalog (de) Deutsch (`de`) joins the interface languages as a complete catalog of 2,035 entries: every command, All tools entry, dialog, panel and notice. It is selected from Preferences, and Auto picks it for `de`, `de_DE`, `de_AT`, `de_CH` and other German locales. Wording uses the informal "du" and the infinitive for commands; translations are clean-room, written from the meaning of the English labels. - de.tsv registered in LANGUAGES (plural rule one|other) - tests: registration and locale mapping, plurals, coverage of every command, All tools label and tl! literal, history labels keeping user values; the Latin UI faces cover umlauts, ß and German quotes; the control-channel language switch includes `de` - ATTRIBUTION.toml/.md entry (kind = "translation"), docs/localization.md, ROADMAP and parity notes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * M14: register German as the 12th language; translate the reopen-session preference (de, bg) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
21a9624585 |
M7: Arabic in added text (shaping, bidi order, searchable Type 3 glyphs) (#403)
Add text wrote Arabic as "?" (standard-14 fonts, WinAnsi). Items with Arabic are now shaped with the craft-fonts Arab face through harfrust (already in the tree via egui) and laid out line by line in display order with unicode-bidi (UAX #9), so Arabic mixed with Latin, digits and brackets reads correctly. Each cluster (a letter with its dots or marks) is one glyph of a Type 3 font drawn from the face's outlines, with a ToUnicode entry, so text extraction, copy and search get every letter once and no font file is embedded. - fonts: shape_arabic / arabic_glyph / arabic_has / document_arabic_font; japanese_glyph's bounded outline code is shared as bounded_outline. - edit: draw_arabic and type3_arabic; wrapping measures shaped words. Fonts get names free on the page (a full save renumbers objects, not resource names) and are removed when the item is updated or deleted. Outlines are read before anything is written. - Without the face, adding Arabic is a clear error that changes nothing; an existing item holding Arabic still moves, drawn as "?" as before. - Non-Arabic items are drawn exactly as before. Refs #250, #280, #369. Co-authored-by: fares <aw674860@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
3de2be8a68 | Issue #442: reopen the files that were open when PdfCraft last closed (#462) | ||
|
|
fa52469dbe |
sign: read the .p12 files Windows writes (BER, not DER) (#453)
Importing a digital ID exported by Windows failed with "PKCS #12: not a PKCS #12 file" — at the second byte of the file, before the password was looked at. Windows writes a .p12 in BER: the PFX, its ContentInfo and the [0] around the AuthenticatedSafe all use the indefinite length form (0x80 … 00 00), and the AuthenticatedSafe itself is an OCTET STRING split into segments. der.rs took definite lengths only, so every one of those files was turned away. OpenSSL writes definite DER, which is why the fixtures never showed it. A Tlv now carries the rules it was read with, and its children are read the same way: Tlv::parse is the strict DER reader it always was, Tlv::parse_ber also takes the indefinite form, and Tlv::octets joins the segments of a constructed OCTET STRING, borrowing when there is nothing to join. Both are depth-bounded. pkcs12 reads the envelope with the BER reader; x509 and cms keep the strict one, so nothing that is verified over its own bytes changes. The MAC decides what is allowed here: it covers the contents of the authSafe OCTET STRING, the segments joined, which is what the writer MACed. So the file is never normalised to DER — that would change the octets the MAC was computed over and turn a right password into a wrong one. The test builds the Windows shape out of the OpenSSL fixtures, twice: the outer wrappers in BER with the MacData intact, which is what proves the MAC is taken over the right octets, and then the AuthenticatedSafe re-encoded as well, with the MacData dropped (RFC 7292 §4 makes it optional), for a file that is BER the whole way down. Claude-Session: https://claude.ai/code/session_013Q5dhMC58jk1ePNW37xe1b Co-authored-by: Fernando Areán <soporte@codebit.es> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5f570eab01 |
M2.6: non-embedded Mincho fonts draw half-width katakana (#484)
Since #368, a non-embedded Adobe-Japan1 font with a Mincho name (HeiseiMin-W3, MS-Mincho, ...) is drawn in document_japanese_font(), i.e. Shippori Mincho. Shippori Mincho has none of the 63 half-width katakana (U+FF61-U+FF9F), which are common in Japanese documents. hayro maps the CID through Adobe-Japan1-UCS2 to Unicode, finds no glyph in the face's cmap, and falls back to drawing the CID as a glyph index: half-width katakana came out as unrelated glyphs (ligatures such as fi and fl). The Mincho substitute is now BIZ UDMincho Regular, which covers half-width katakana (and was designed as a metric-compatible replacement for MS Mincho), falling back to document_japanese_font() when it isn't built in. The face used for Japanese text written into PDFs is unchanged. Without craft-fonts nothing changes. Tests: raster::tests::mincho_substitute_covers_half_width_katakana (with CRAFT_FONTS_DIR: the Mincho substitute's cmap maps U+FF61-U+FF9F and 日本; it lacked all 63 half-width katakana before; skips without craft-fonts). skrifa 0.44 is a dev-dependency of pdfcraft-render for it, the version pdfcraft-fonts already uses. The parity entry view.render-cid-fonts cites the test. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
271139bd3b |
Comments: a new highlight opens its note for typing (#475) (#481)
Adding a highlight selected its card in the Comments panel but left the note closed; the text field only appeared after double-clicking the card. Acrobat opens the note for typing as soon as the highlight is made. apply_edit now opens the inline editor for a new highlight (text drag, selection then tool, area highlight all pass through it), unless a note typed into another card is still unsaved, so that text is never dropped. |
||
|
|
438a269bac |
M7: report Japanese Type 3 fallback font names (#387)
Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
85dcb9dece |
Issue #295: three clicks select the line, four clicks the page (#366)
* Text: find the word and the line around a glyph PageText gets word_at(i) and line_at(i). Each returns the first and last glyph of the word or the line around glyph i, or None when i is past the end of the page's text. A word stops at a word gap, a blank glyph or a line end. A line is the run of glyphs with the same line number. The canvas's double-click used its own loop for this, which indexed the glyph, line and gap arrays directly. It now calls word_at, which reads them with get(). Double-clicking selects the same words as before. Test: render words_and_lines_around_a_glyph, on two lines with a blank glyph, a word gap and a line break. * Issue #295: three clicks select the line, four clicks the page With the Select tool, quick clicks on text widen the selection like Acrobat: two clicks select a word (as before), three the line, four or more all the text on the page. A third click used to clear the selection. egui counts only up to triple clicks and reports a fourth quick click as another triple, so the view keeps its own count of the current run. A single click resets it and a pause ends it. With the markup tools (Highlight and the like) and Redact, a double-click already turns the word into a mark at once. They stop at the word, so more clicks don't stack line or page marks. Tests: ui-egui quick_clicks_select_a_word_then_the_line_then_the_page and quick_clicks_with_the_highlighter_mark_only_the_word. parity: view.text-select cites the tests. The notes say what is missing: paragraph selection, and Ctrl/⌘+A across the whole document (it selects the current page). * Clippy: as_chunks in the signature preview test (follow-up to #355) Clippy 1.99 rejects chunks_exact with a constant chunk size (chunks_exact_to_as_chunks), so `cargo clippy --workspace --all-targets -- -D warnings` fails on main since #355. Use as_chunks, as #360 did for the test above it. --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
31f3631868 |
Home: pin folders and list their newest PDFs (#383)
* Home: pin folders and list their newest PDFs
File > Pin folder to Home... (or "Pin a folder..." on Home) pins up to 8
folders. Each lists its newest PDFs with how long ago and size, opens one
with a click, and can be unpinned. Folders are read on a worker thread and
listed again every few seconds while Home shows, so new scans appear by
themselves; an unreadable folder (moved, offline synced drive) stays pinned
and says so. A folder is described by where it is ("iCloud Drive",
"OneDrive - Contoso", "Documents > Work") instead of its full path.
Cloud drives work through their local synced folder; nothing goes through a
provider API. Settings keep absolute paths only, deduplicated and capped.
Tests: crates/ui-egui/tests/folders.rs (8) and folders_ui unit tests (4).
Command label translated in every catalog; Home strings in fr, es, ja,
zh-hans and zh-hant; catalog hashes updated in ATTRIBUTION.toml.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add my contributor name (amitabhthakur)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Home pinned folders: Russian strings; regenerate ATTRIBUTION.md
The Russian catalog (#375) landed while this branch was open: its coverage
tests need the new command label and Home strings. ATTRIBUTION.md is
regenerated (cargo xtask assets --write), which also adds the ru.tsv row
that #375 left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
07b95409bf |
Comments without an appearance stream are drawn (display only, never saved) (#371)
* M5: comments without an appearance stream are drawn (display only, never saved) #260 (page 7): a FreeText box with /DA and an orange /C background, a Text note and an Ink stroke, all without /AP, were not drawn; Acrobat, MuPDF, PDFium and Poppler draw them. The renderer only draws /AP (plus the Highlight fallback of #274), and pdfcraft-annot, which already draws all of these for PdfCraft's own comments, can't be used from the renderer (both are L3). pdfcraft_annot::with_missing_appearances returns a copy of the document in which every annotation without /AP /N that appearance::build can draw (FreeText, Ink, Text, FileAttachment, stamps, shapes, lines, markup, …) has the appearance build draws from its dictionary; links, form fields and pop-ups are left alone, as is anything build declines. The engine keeps that copy, written as an in-memory incremental update, in a new Document::display: the page view's render pool, page_render and page images (export_source) read it. Document::bytes, what Save, signatures and the inspector use, is unchanged. display is rebuilt wherever the renderer was (open, refresh, scoped refresh) and is bytes itself when nothing is missing or the document can't be edited. Tests: annot tests::comments_without_appearances_get_one_for_display_only (FreeText, Ink and Text get /AP /N in the copy, an existing /AP is kept, Link, Widget and an unknown subtype aren't touched, the original is untouched, none means no copy) and engine tests::comments_without_appearances_are_drawn_but_not_saved (the page view draws the FreeText background and the Ink stroke, before and after an edit; the working bytes stay the opened file; Save writes no /AP). On main the page view shows white where the FreeText box is. Parity: view.render-annotation-appearances cites the test and notes what's left (Link borders without /AP, printing). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the Ink stroke on its apex row: Ink is now a curve through its points (#374) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
3b8a274f52 |
M2.6: non-embedded Japanese CID fonts draw in a craft-fonts face (#368)
#260 (page 2): text in a Japanese CID font that the PDF doesn't embed (HeiseiMin-W3 with UniJIS-UCS2-H, Adobe-Japan1) drew nothing, while MuPDF, Poppler, PDFium and Acrobat draw it in a Mincho face. hayro asks its font resolver for a substitute with the CID collection in the query, but the renderer never installed one, so hayro's default answered with a Latin standard-14 font that has no Japanese glyphs. Release builds have embedded Japanese faces from craft-fonts all along; the renderer just didn't use them. RenderConfig::settings now installs a resolver: for an Adobe-Japan1 fallback query it returns a craft-fonts face (Shippori Mincho for Mincho names, BIZ UDPGothic Regular or Bold for Gothic names, the descriptor's serif flag otherwise); every other query keeps hayro's default. hayro already maps such fonts through the Adobe-Japan1-UCS2 CMap to Unicode and the face's cmap. Without craft-fonts nothing changes. Other CJK collections are untouched: the pinned craft-fonts has only Japanese faces, and its later Chinese face is Noto CJK, which AGENTS.md §1.1 rules out. pdfcraft-render now depends on pdfcraft-fonts (L3 to L2). Tests: raster::tests::non_embedded_japanese_cid_fonts_draw_with_a_craft_fonts_face (with CRAFT_FONTS_DIR: 日本 in HeiseiMin-W3 and HeiseiKakuGo-W5 now draws; it drew 0 dark pixels before; skips without craft-fonts, as the Linux CI job has them) and raster::tests::japanese_font_names_pick_mincho_or_gothic. The parity entry view.render-cid-fonts cites the test and notes what is still missing. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
5ef27ff678 |
M5: ink strokes are drawn as smooth curves through their points (#374)
#260 (page 7) compares PdfCraft with Acrobat, which draws an Ink annotation's /InkList as a smooth curve through the points; pdfcraft-annot drew straight segments between them (as MuPDF, Poppler and PDFium do). A stroke of three or more points is now drawn as a Catmull-Rom spline through every point, written as cubic Béziers: the curve leaves each point parallel to the chord between its neighbours, and an end uses itself as the missing neighbour. Two points stay a line and one point a dot. This is the appearance PdfCraft writes for its own ink, signatures drawn with Fill & Sign included, and, with the display-only appearances for comments that have none, how ink without /AP is shown. A curve can swing beyond its points, so a new ink annotation's /Rect now also covers the curve's control points (the curve stays within them); before, only the points plus the line-width margin. Test: annot tests::ink_strokes_are_smooth_curves_through_their_points checks the exact curve for a three-point stroke, a line for two points, a dot for one, and a /Rect that holds a stroke whose curve rises above its highest point (22 on main, 23.667 now). The comment.pencil parity entry cites it. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
4a50222258 |
Forms: round required-field border on radio buttons; Highlight fields is remembered (#372)
* Forms: round required-field border on radio buttons; Highlight fields is remembered #260 (page 8) compares PdfCraft with Acrobat, which outlines required checkboxes and radio buttons in red. Adobe's documentation says that border shows "when the Highlight Existing Fields button is selected", which PdfCraft already did (View: Highlight fields). Two differences remained: - The red border was a rectangle for every widget, so a round radio button got a square around its corners; Acrobat's follows the button. Radio buttons now get a round border and tint. - Acrobat keeps Highlight existing fields as a preference, while PdfCraft forgot it for every document. The choice is now remembered for the next document and the next session (ViewDefaults and settings). It still defaults to off, and red borders still appear only while fields are highlighted. Tests (view.rs): required_radio_buttons_get_a_round_red_border (the widget's corner, outside the circle, is not red; on main it is) and field_highlighting_is_remembered (a second document and a restored session open highlighted; on main the second document didn't). The form.highlight-fields parity entry cites both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * view.rs: the field-highlight tests sit with the others (no merge conflict with #363) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
2a35a1a76f |
M11: Optimize PDF and Reduce File Size run in the background with a progress bar (#370)
* M11: Optimize PDF and Reduce File Size run in the background with a progress bar
Advanced optimization and Reduce File Size did all their work on the UI
thread, so on a large file the window stopped repainting until they were
done and looked as if it had crashed.
- pdfcraft-optimize: optimize_with_progress reports each image and the
clean-up; returning false stops with OptimizeError::Cancelled.
- engine: Session::optimize_job captures a copy of the document and the
choices; OptimizeJob::run reports each OptimizeStage (removing user data,
images, cleaning up, merging, writing), stops on request with
EditError::Cancelled and turns a panic into an error so a worker always
reports back. optimized_bytes is the job run in place.
- ui: both commands run the job on a worker thread (inline in tests and on
the web), like OCR and Export. A card at the bottom centre shows the
stage ("Optimizing... image 14 of 40"), a bar that eases towards the
fraction with a light sweeping across it, the percentage and Cancel;
notices stack above it. A second run is refused while one is going. The
control channel's state includes the progress.
The headless doc_optimize and doc_reduce tools stay synchronous. New
strings are translated for es, fr, ja, zh-hans, zh-hant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Clippy: as_chunks in the signature image background test (follow-up to #355)
#360 switched the image test to as_chunks; #355 added another chunks_exact(4)
in the background test, which clippy's chunks_exact_to_as_chunks rejects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Optimize: report a failed worker spawn instead of waiting forever
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
|
||
|
|
f88a59f762 |
M10.2: Create PDF from multiple files, edited as pages in the Organize grid (#361)
* M10.2: create PDF from multiple files Create a PDF ▸ Multiple files takes PDFs, images and .txt files, lets the user reorder them, and makes one combined PDF (a bookmark and an optional page range per file) or one PDF per file in a chosen folder, never overwriting what is there. - create: source_kind() tells PDFs, images and text apart (moved from the shell) - engine: Session::convert_to_pdf, shared by Open, the dialog and the tool - automation: doc_create_multiple (combine or separate) - ui: Multiple files dialog reusing the Combine files list; translations - parity entry and ROADMAP updated; catalog hashes refreshed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M10.2: multiple files open in the page grid; insert files at any gap Create a PDF ▸ Multiple files now opens the picked files as one unsaved document in the Organize page grid instead of a file-list dialog: pages are removed and reordered there, and Save pages writes what is shown. - grid: a "+" at every gap inserts files right there; Save pages button - insert from file: several files in order, images and text converted, inserted pages selected - the file-list dialog is removed; one PDF per file stays in the doc_create_multiple tool - translations, catalog hashes, parity notes and ROADMAP updated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M4: insert files by dropping them on the page grid On desktop, PDFs, images and .txt files dropped on the Organize page grid are inserted at the gap under the pointer, in order, and selected; a dropped file still opens as a document everywhere else. winit does not report the pointer while files are dragged over the window on every platform, so a drop waits (up to a second) for the pointer to show up and otherwise appends at the end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * M4: files dragged over the page grid follow the pointer (macOS) winit sends no pointer moves while files are dragged over the window, so egui kept the place the pointer entered at: the insertion bar froze there and the files were inserted there. During a file drag the pointer is now read from AppKit (safe objc2 calls, crates already in the tree) and fed to egui, so the bar and a "+" badge follow the pointer and the files go where they are released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
acb8e96aff |
Update image signatures live during resize and movement (#355)
* M5.7: update image signatures live during resize and movement * M5.7: preserve image signature proportions when resizing corners --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
05c8ab3b3e |
M14: Telugu (తెలుగు) interface language (#349)
* M14: Telugu interface font - The interface takes `Telu` faces from craft-fonts after the CJK and Arabic ones, on the web too - Test: Telugu text has glyphs in every family and its conjuncts are shaped Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> * M14: Telugu interface language - Complete te catalog: 1,984 entries plus whole-phrase Undo/Redo; te and te_IN locales use it - Undo and Redo labels use a catalog phrase when the language has one (Telugu puts the verb last) - Tests, docs, attribution, parity and ROADMAP Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> * Add my contributor name (chiranjeevi-max) Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> * M14: catalog contexts for labels with two meanings - tl_ctx! gives the signature pad's Type, the Action Wizard's Start and the comment menu's Edit their own context; Telugu translates them, other languages keep their current translation - Preferences' All tools goes through the catalog in every language Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> * M14: Telugu for the look-alike web address warnings Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> * M14: Telugu for the image signature notices Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> * Telugu for Open Recent (#323) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
dbe4b2dddb |
Issue #290: File ▸ Open Recent lists the recent files (#323)
* Issue #290: File ▸ Open Recent lists the recent files The Home view kept a recent-files list but the File menu had no way to reach it; Acrobat shows File ▸ Open Recent and the request asked for exactly the menu wiring. A new command file.open_recent renders as a submenu of the live list right under Open… (disabled while the list is empty); each entry opens its file or focuses the tab already showing it, which is now shared with the Home view's list through the new open_recent() instead of a second copy of that check. The command palette runs it as "open the most recent file", or says there are none. Translations added to every catalog (Japanese, Czech, Spanish, both Chinese and Brazilian Portuguese are test-covered). Test: commands.rs::the_open_recent_menu_lists_files_and_opens_one (submenu lists a seeded recent file and clicking it opens a new active tab on that path) and commands.rs::the_open_recent_menu_is_disabled while_the_list_is_empty. Both failed before the change (no such menu). Co-authored-by: Mawfyy <mawfy12@gmail.com> * Build: cap cargo jobs and lighten the dev profile On a 7.1 GiB machine the default one-job-per-core (12) with every dependency at opt-level 2 plus full debuginfo swap-thrashes the box frozen mid-build. jobs = 4 bounds peak memory to a few GiB and matches GitHub runners (4 vCPUs), so CI is unaffected; dependencies drop to opt-level 1 with debug = false, which halves their build cost and keeps them fast enough for a usable debug app. Own crates keep their opt-level and full debuginfo. Verified: a from-scratch rebuild of all dependencies peaked at ~2 GiB used with the machine responsive throughout; the perf.rs frame-budget tests still pass at the lower dependency optimization. Co-authored-by: Mawfyy <mawfy12@gmail.com> * Keep the build configuration as on main; French for Open Recent The jobs and dependency opt-level changes are a separate build-policy decision. French is coverage-checked since #291; catalog checksums refreshed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * French for Open Recent again, and catalog checksums refreshed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mawfyy <mawfy12@gmail.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e17833a64e |
Add image signatures with placement preview and resize controls (#335)
* M5.7: import images as Fill & Sign signatures and initials * M5.7: preview image signatures and select after placement |
||
|
|
bb8fcd006e |
Security: refuse mailto attachments, show link hosts in punycode (follow-up to #104) (#337)
* Security: refuse email links that attach or insert a local file Follow-up to #104. A document's mailto: link could ask the email app to attach a file from the user's computer (Evolution, KMail and Claws Mail honour attach=, Evolution and KMail also attachment=) or read one into the body (Claws Mail's insert=). document_url now refuses a mailto: address in which any word starting with attach or insert (any case) is followed by =, wherever it stands and after each layer of percent-decoding. It also refuses text that some email tools decode before reading the address: RFC 2047 encoded words, which KMail decodes across the whole address, and backslash escapes, which xdg-email's echo decodes. The link is refused rather than rewritten: PdfCraft doesn't change what a document asks to open, and the email apps' parsers disagree with one another. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Security: show a link's host as the browser receives it, in punycode Follow-up to #104. The link prompt's host now comes from links::display_host, which reads it with url::Url: the webbrowser crate hands the OS that crate's serialisation, and the web build's browser parses by the same WHATWG standard. International hosts are shown in punycode with a note, a label mixing writing systems (UTS #39 resolved script sets) gets a red warning, numeric hosts such as 3232235777 show as 192.168.1.1, and a host the browser would reject is refused as malformed. The Unicode form isn't repeated in the prompt, so a whole-script lookalike can't read as the real site. url, idna and icu_properties become direct dependencies of pdfcraft-engine; all three were already in the graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Security: refuse document addresses the URL parser rejects Follow-up to #104. egui opens links through the webbrowser crate, which parses the address with url::Url and, when that fails, treats it as a local file path and opens file:///<cwd>/<address> in the browser. document_url accepted addresses that don't parse, such as mailto://a^b/x or https://a^b/ (a forbidden host code point), so a document could still get the browser to open a local path. document_url now also requires url::Url to accept the address with the same scheme, which is exactly the parse webbrowser does first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
a2d92daa00 |
Issue #263: combine, extract and split keep the PDF/X output intent and identification (#334)
Combine, extract and split built the new document from copies of the pages and carried bookmarks, fields, layers, attachments and four Info keys, but not the catalog's /OutputIntents, the GTS_PDFXVersion, GTS_PDFXConformance and Trapped Info entries, or the XMP pdfxid identification. A PDF/X file stopped being one, and a RIP or preflight lost the intended output condition. Extract and split now carry the output intents (with their ICC profiles, through the same object map as the pages), the Info entries (the version read from Info, else XMP) and a new XMP packet holding the identification, trapping and title. The source's packet is not copied: it can claim standards the new file no longer meets, such as PDF/UA, whose structure tree is not copied. Combine carries the first source's standard only when every source declares the same version, trapping and output intents (type, condition and profile bytes). Not yet: a warning when combine's sources disagree, and XMP dates. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
9140b44792 |
M4: Combine files in its own tab, with a sortable table and unlocking of protected files (#321)
* M4: Combine files in its own tab, with a sortable table and unlocking of protected files Combine files was a modal dialog with a cramped list and up/down buttons on every row. It is now a tab next to Home and the documents, showing a table of the files: File name | Pages | Size | Modified | Warnings. - Columns sort when their heading is clicked (anywhere in the cell; again to reverse; names in natural order), resize from their edges and move by dragging the heading. Order and widths are kept in the settings; a right-click on a heading resets them. - Rows select like a file manager (click, Ctrl/Cmd+click, Shift+click, Shift+arrows, Ctrl/Cmd+A). One toolbar moves, removes and unlocks the selection; Alt+arrows and dragging move it too. Every change to the list undoes and redoes with Cmd/Ctrl+Z (also toolbar buttons). - Add files, Add folder, Add folder and subfolders (a split button), Add open documents, or drop files and folders on the tab. - Problems show before Combine: password-protected files, security settings that withhold page assembly, page ranges (checked as typed, with the pages they take), duplicates. Files that open but are protected say that the combined file won't keep their security; the result is never encrypted. - Unlock... asks for a file's open or permissions password (one password can be tried on several files). Passwords stay in memory, are never printed, and reach the engine through combine_unlocked; doc_combine takes them as `passwords`. The engine decides whether a password works, since the inspector rejects some it accepts (RC4 owner passwords). organize.combine-sort is shipped and organize.combine-options partial (include open files). Strings are translated for es, fr, ja, zh-hans, zh-hant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Credit Marlon Konrad (mlkonrad) in the About window Adds my own entry to contributors/people.toml, as docs/contributors.md asks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
f70c4caac9 |
Security: create save staging files with create_new (follow-up to #164) (#319)
* Security: create the --root staging file with create_new Follow-up to #164. write_atomic staged every save at the fixed name .<name>.pdfcraft-tmp and wrote it with fs::write, which follows a symbolic link and writes through a hard link. So a link planted there, in a folder someone else can write to, redirected the bytes to any file the link pointed at. The staging file now gets a random 64-bit suffix (std's RandomState, keyed from the operating system's random source) and is opened with OpenOptions::create_new, which fails if anything already has the name (a file, a hard link, a symbolic link even when dangling), on Windows (CREATE_NEW) as on Unix (O_EXCL). A taken name is skipped, never opened; Windows reports a folder at the name as "access denied", which counts as taken when something is there. After 16 taken names the save fails and nothing is overwritten. A failed write or rename removes only the file this save created. At most 128 bytes of the target's name go into the staging name, so it fits every system's name limit. The #164 tests that planted a sentinel at the old fixed name now also compare folder listings, and say that the "is a folder" refusal is what they check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Security: create the desktop app's save staging file with create_new The same fix as the previous commit, for the desktop app. editing::write_atomically, which Save, Save As, Export, Print to PDF, signing and crash recovery all use, staged at .<name>.pdfcraft-<pid>.tmp and opened it with File::create, which follows a symbolic link and writes through a hard link. A process id is easy to guess, so a link planted beside a document in a shared folder could redirect a save into another file. The staging file now gets a random 64-bit suffix and is opened with OpenOptions::create_new; a taken name (on Windows, a folder reports "access denied") is skipped, never opened, and after 16 the save fails without overwriting anything. At most 128 bytes of the document's name go into the staging name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
81a6ca456c |
M7: preserve ideographic spaces in edited paragraphs (#309)
Co-authored-by: Codex <noreply@openai.com> |
||
|
|
87f635cf65 |
XFA: run FormCalc scripts (#301)
* XFA: run FormCalc scripts A FormCalc interpreter (XFA 3.3 part 8) in the js crate, over the same form table, effects and budgets as the XFA JavaScript model, so the engine runs both languages alike: expressions and FormCalc coercions, var, if/while/for/foreach/func, accessors ($, $host, $form, $record, $layout, $event, xfa., .., [n], [*], _name instance managers) and the arithmetic, logical, string, date/time, financial and unit built-ins. Get/Post/Put refuse. The JavaScript model's XHost, HNode, SOM resolution and instance operations are shared (pub(crate)) with Context-free add_instance and remove_instance that the boa wrappers call. Bounded like the JavaScript path: steps, loop iterations, recursion, parse depth, string and list sizes, clamped dates, the instance cap, and the per-activity time budget with abandonment. Tests: language, built-ins, control flow, form objects and hostile scripts in the js crate; a FormCalc fixture form in the engine that initializes, calculates, validates, adds and removes rows, shows a subform and round-trips through save; hostile scripts at the engine level. parity form.xfa-formcalc planned -> partial. Hardened after two adversarial review passes: statement nesting and chain length count against the limits (verified on a 1 MiB stack), the script honours the engine's deadline itself, strings are capped after every built-in and messages/values clipped and noted (shared host), lists hold each object once and the shared SOM resolver deduplicates, values go into fields only (shared host, JavaScript too), saturating Stuff and time arithmetic, FormCalc-specific size check instead of the JavaScript refuse scan. Semantics: bare row[*] and row[n] from inside a row see every row, relative [-n]/[+n] indexes, Count of non-null, Parse with a date picture gives the ISO date, assignment is an expression. * form.xfa-formcalc: bound FormCalc values, results, errors and copying (#301 review) - XHost::set_value cuts a value to 64 Ki chars before storing it, so later reads match what is saved and the snapshot stays bounded. - A script's completion value (calculate result) is cut to 64 Ki chars with a note, in both the FormCalc and the JavaScript paths. - throw clips its value like any message. - Copying, dereferencing and comparing long strings is charged to the step budget (len/256), so wasm (no clock) can't spend unbounded time on it; OneOf charges per comparison. - Exists propagates step/deadline/depth/size limits, answering 0 only for lookup failures. - IPmt/PPmt charge a step per period. - The parser counts each top-level expression's chain from zero, so an elseif condition no longer inherits the previous arm's count. - Comment on the interpreter's depth limit fixed; parity notes say memory isn't capped and Uuid/Date/Time are deterministic on wasm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
edce549a32 |
M9: sign with the Windows certificate store (#179) (#304)
List Current User Personal CNG identities alongside file digital IDs and sign without exporting their private keys through safe rustls-cng wrappers. Expose Windows store references through automation and the desktop dialogs, with RSA and ECDSA validation, certificate cleanup, and UI regression coverage. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |