Commit Graph
510 Commits
Author SHA1 Message Date
7aac682ef8 M7: Cut, Redact, Create Link and Recognize Text in the picture menu; keep the menu open (#1621)
* M7: add Cut, Redact, Create Link and Recognize Text to the picture menu; keep the menu open

The right-click menu of a picture in Edit text & images gains Cut Image,
Redact Image (a redaction mark over the picture), Create Link… and Recognize
Text… (for the picture's page), with translations in every catalog.

The menu was drawn only while the pointer was over the picture, so an item
outside it, which is most of a small picture's menu and Delete once the menu
reaches below the page, could not be clicked. An open menu is now drawn
wherever the pointer is.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Update the catalog checksums in ATTRIBUTION.toml

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Fix the merged test file and checksums

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 03:47:20 -04:00
10ad9b46e5 Keep added text selectable over form fields (#1528)
* Keep added text selectable over form fields

Generated with Codex for @FredApps.

* Fix the content.rs test harness merge: harness_bytes and harness_with_step_dt share one builder

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>
2026-10-15 03:42:56 -04:00
b7976ef40e M12: show structure tag content (#1496)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:38:35 -04:00
980bd90087 Print: 4 x 6 in label paper and a Label layout for shipping labels (#1583)
* Print: 4 x 6 in label paper and a Label layout for shipping labels

Thermal label printers (e.g. VEVOR Y486) could not be used for eBay/carrier
labels: the paper list had no 4 x 6 in size, the chosen paper was never sent
to CUPS (the queue's default was used), and labels laid out sideways on a
Letter/A4 sheet printed small or rotated.

- PAPERS gains "4 x 6 in label (101.6 x 152.4 mm)"; cups_media() sends the
  chosen sheet to the spooler as `-o media=` (Letter, Legal, Tabloid, A3-A5
  and w288h432 for the label), so the driver feeds the size the PDF was
  laid out on.
- New Layout::Label: each page is cut to its printed area (a low-resolution
  render, as Fit Visible does), turned a quarter turn when it is sideways in
  the direction that makes its text upright (text_reads_up reads the text
  and form matrices), and fitted uniformly with a small margin. Vector
  content is kept, so barcodes stay sharp and undistorted. "Turn upside
  down" flips it 180 degrees. One label per sheet.
- Choosing the label paper selects the Label layout, and vice versa.
- Strings translated in every bundled catalog.

Tests: label geometry (crop, quarter turns, no stretching, margins),
cups_media, text direction; print, ui-egui and automation unit tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFXSksdMaJ3YnheY7Q6sTx

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Alessio Boschi <alessio.boschi@dinova.one>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 03:38:06 -04:00
24f93e6102 feat(ui): preference to hide author-only highlight hover previews (#1432) (#1466)
* feat(ui): preference to hide author-only highlight hover previews (#1432)

* Keep the Identity heading, translate the new hover preference, move its persist key out of json!

The heading rename dropped the existing Identity translation in every catalog; the
new checkbox label now has an entry in every catalog. The json! object in persist()
is at serde_json's recursion limit, so the new key is set after it like redact_prefs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cargo fmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:33:43 -04:00
a6124bcdfe M12: find untagged accessibility content (#1463)
* M12: find untagged accessibility content

* i18n: translate untagged content checker (rebased)

* fix(i18n): refresh untagged content translation hashes

* fix(tests): restore full test modules for find-untagged content

* fix(i18n): preserve existing translations after rebase

* ci: rerun shared timeout test

* chore(deps): refresh rust for the advisory gate

Use the current lockfile, including the rustls security update, so the dependency check can run on this branch.

* Refresh catalog hashes after merging main

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:33:19 -04:00
afa50999ef M12: tag unstructured AcroForm widgets (#1451)
* M12: tag unstructured AcroForm widgets

* i18n: translate form tag command

* i18n: repair form-field label merges

* fix(i18n): refresh translation catalog hashes

* fix(i18n): preserve main translations after rebase

* Merge main; tag a widget listed on several pages once, docs say existing trees are extended

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Merge main; add the Vietnamese catalog entry for Tag form fields

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>
2026-10-15 03:24:55 -04:00
416c85579b Perf: defer marker and link index scans until first use (#1434)
* Defer secondary PDF indexes until first use

* Merge main into #1434; read Document::added through the new method in content_ui

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>
2026-10-15 03:20:32 -04:00
ae9fb42688 M3: scroll the view when drawing at an edge; take back a polygon's last point (#1549)
While a comment is being drawn, the pointer in a band along any edge of the
viewport (or just past it) scrolls the view toward that edge, so a point
outside what is shown can be reached. Polygon, cloud and connected-lines
drawings take back their last point with Backspace, Delete or Undo; removing
the only point cancels the drawing.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 03:19:50 -04:00
41809cf765 fix(macos): Cmd+Q asks about unsaved changes (#1136) (#1533)
* fix(macos): Cmd+Q asks about unsaved changes (#1136)

winit's default app menu sends Quit (Cmd+Q) straight to
-[NSApplication terminate:], so PdfCraft quit without asking and
unsaved edits were lost; the close button and Dock > Quit already ask.

Install craft-quit's app menu (storytold/craft-libs#21) from the app
creator. Its Quit sends ViewportCommand::Close, which guard_quit
handles like the close button. If the menu can't be built, winit's
menu stays and a warning is logged.

craft-quit is pinned to the head of craft-libs#21 until #5 and #21
are merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(macos): closing the window quits through terminate: (#1136)

On Touch Bar Macs the close button crashed PdfCraft (exit 133,
-[_NSTouchBarFinderObservation invalidate]): eframe drops the window
inside AppKit's run loop. craft_quit::on_close_request, right after
guard_quit in `logic`, cancels an allowed close and quits through
-[NSApplication terminate:] instead. A close that guard_quit holds for
the unsaved-changes prompt is left alone. Elsewhere it does nothing.

craft-quit moves to the workspace dependencies, pinned to craft-libs
main (f58bc1a, with #5 and #21 merged) instead of #21's head.

The on_close_request change and its Touch Bar test are from jmacek.

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>
2026-10-15 03:15:34 -04:00
02a4d81a22 Keep selected form values and page content through later edits (#1319, #1305, #1324, #1306) (#1452)
* Keep selected form values and page content through later edits (#1319, #1305, #1324, #1306)

An XFA radio item whose text contains a space was written back as an appearance-state name, so the next layout selected nothing. Replace Pages filled the gap between a split selection. Marking a figure decorative replaced every content stream on the page. Control clicks and drags accepted coordinates that overflow to infinity.

* Move the decorative-figure regression out of pdfcraft-edit

edit and a11y are both layer 4, so a dev-dependency from edit to a11y fails the layer check. The same regression now runs in the engine, which already depends on both.

* Drop pdfcraft-a11y from the edit crate's lockfile entry

* Run a tool after a background open of the file it asked for

A tool chosen with no document opened the picked file on the worker, but only ran when that open finished inside the picker callback. Remember the command on the pending open, and let the command test wait for the worker.

* Wait for background opens in the command tests

Opening a picked file or a recent file finishes on the worker, so a fixed handful of frames can assert before the tab exists.

* Compare batch copies to the PDF that was written

A new text PDF stamps the current second, so a later pdf() call is a different file. The numbered copy and the source must match the bytes that were saved.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:05:21 -04:00
9b6dab142f M10: Windows print jobs show per-sheet progress; printers list faster (follow-up to #944) (#1236)
* M10: Windows print jobs show per-sheet progress; printers list faster

Follow-up to #944, carrying the two parts main doesn't have yet.

- The progress card follows a job being spooled and counts its sheets
  as they reach the spooler ("Sending sheet 2 of 3 to Office…"), using
  craft-print's new submit_with_progress. It replaces the "Sending to…"
  toast, leaves another long job's card alone, and goes when the job
  ends; the spooler's answer is still a notice.
- craft-print bumped to storytold/craft-libs#27, which also lists
  Windows printers through .NET's PrinterSettings (the API jobs spool
  with) instead of a CIM query of Win32_Printer.
- "Sending sheet {s} of {n} to {printer}…" in all 20 catalogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 03:04:29 -04:00
db34952a94 M6: expose dropdown and list-box field alignment (#1208) (#1263)
Reuse the existing alignment picker for choice properties, keeping existing /Q and choice values until changed. Cover cancel, undo/redo, save/reopen and retained options/flags through the real UI.

Generated with Codex.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:01:17 -04:00
6b185cdc5b Add permission-aware selected-text Copy to Edit and context menus (#1244)
* M3: add selected-text Copy to page and Edit menus (#1225)

Register selected page-text Copy with the existing document permission gate and expose it in Edit and the ordinary page context menu. Route floating Copy through the same gate while preserving focused-input and Organize clipboard ownership.

Four regressions fail behaviorally on the exact base; five candidate regressions pass. All eight cargo xtask ci stages pass: 2575 tests passed, 12 ignored, and 31 WASM crate checks. Native synthetic checks verify Unicode clipboard output, disabled no-selection/restricted actions, and permissions-password unlock using presented Xvfb/Vulkan software-rendered frames. Physical display and hardware GPU coverage are not claimed; the optional cargo-deny audit was unavailable.

Generated with Codex for FredApps.

* Tests: scope GoToR case mutation to Unix for Windows Clippy

Keep the common directory/non-PDF cases immutable. Shadow them in the Unix-only block so FIFO coverage can append its case without an unused-mut warning on Windows. No assertion or platform coverage is removed.

This repairs the exact hosted Windows Clippy failure inherited from upstream. Source review and whitespace checks passed; no local build, test or formatter was run during the pause. Automatically triggered hosted CI will validate this head separately.

Generated with Codex for FredApps.

* Tests: scope document link target mutation to Unix for Windows Clippy

Preserve directory coverage on all native platforms and device coverage on Unix. Static diff, whitespace and conflict checks passed; no local build, test or formatter was run during the pause.

Generated with Codex for FredApps.

* Tests: escape GoToR fixture file paths as PDF literal strings

Windows absolute paths were interpreted as PDF escape sequences, introducing control characters and failing the hosted local GoToR root test. Escape backslashes and parentheses in the fixture helper without changing product behavior or assertions. Static diff and whitespace checks passed; hosted CI will validate the repair. No local builds, tests or formatters ran.

Generated with Codex for FredApps.

* Tests: use ordinary absolute paths in the GoToR fixture

Build the absolute target from the original sandbox base rather than its canonicalized Windows verbatim path. Assert absolute-path form and canonical identity with the sandbox target. Preserve production UNC/device/verbatim rejection and existing containment and destination assertions. Static diff and whitespace checks passed; no local build, test or formatter ran.

Generated with Codex for FredApps.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:00:45 -04:00
e08f87f138 M5: Make Properties Default in Comment Properties (#2031)
* M5: Make Properties Default in Comment Properties

Acrobat's Comment Properties dialog has a Make Properties Default box
beside Locked. Ticking it and pressing OK makes the edited colour,
opacity, line width (and, for shapes, fill) the default for new comments
of that kind, e.g. new strikethroughs take the chosen colour.

The dialog applies the edited look, so the box also works when only the
default changes. Opacity and width go through the clamping setters: the
dialog allows 0% opacity, which would make an invisible default.

The box is translated in all 21 catalogues and the comment.default-properties
parity entry names the new test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:59:01 -04:00
cortenburg28-archandClaude 00442a0e9b fix(ui): a shape just drawn resizes from its handles without the Select tool (#2362)
Drawing a rectangle or oval selects it and paints its eight handles, but only the Select tool
answered them: with the shape tool still on, dragging a handle drew a second shape on top of the
first. The handles were a promise the tool did not keep, and anyone marking up a drawing had to
switch tools after every shape to fix its size.

A shape tool now hands a press on the selected comment's handles to the same select_input the
Select tool uses, as the Fill & Sign tools already do for their marks. Only the handles: a drag
anywhere else, over the shape included, still draws, so drawing one shape inside another keeps
working, and a polygon being clicked out keeps its clicks. The Eraser is left out. The handle
test is shared with `grabs` rather than copied.

Lines, arrows and polygons are unchanged: they have no resize handles with the Select tool
either.

Fixes storytold/pdfcraft#2338


Claude-Session: https://claude.ai/code/session_01DsT1y8xBvkXeBxEUQqdDtj

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-15 02:57:54 -04:00
Earl Co 1262aa8cf7 M10.5: convert print previews to grayscale when Print in grayscale is set (#2351)
The Print dialog dimmed the colour page instead of showing the monochrome
output that CUPS or the Windows spooler will print. Preview requests now
carry a grayscale bit when the box is checked; the canvas converts the
raster to luminance on receipt. Premultiplied RGB is unmultiplied first,
so translucent content keeps its paper-relative luminance, and toggling
the box drops colour textures for the correctly tagged replacement.
2026-10-15 02:57:12 -04:00
bfe61bebc5 M3: arrow keys follow the Scroll speed preference (#985) (#2333)
* M3: arrow keys follow the Scroll speed preference (#985)

The preference set how far a wheel notch scrolls, but the arrow keys
always moved 40 points. They now move 40 points times the preference's
factor (half at Slow, double at Fast), Page Down / Page Up still move a
screen, and the preference is labelled for both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCdPYhYkggLJVYoME2kr5R

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:55:49 -04:00
b33445aa2e fix(print): the Print dialog waits for the printer list instead of offering Save as PDF (#160) (#2332)
* fix(print): the Print dialog waits for the printer list instead of offering Save as PDF (#160)

The first time the dialog opens, the printer list is read in the
background and no printer is selected yet, so the dialog showed Save as
PDF and its button saved a PDF. It now says it is looking for printers
and the button waits until the list arrives or the user chooses Save as
PDF. An empty list says that no printers were found. The button's
Print / Save as PDF label is translated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCdPYhYkggLJVYoME2kr5R

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:55:22 -04:00
Krzysztof Cieślik 331813e5de fix(ui): stop dropping cached tiles when zooming away and back (#2196) (#2323)
- Retain tiles of on-screen pages at both eviction sites in
  crates/ui-egui/src/canvas.rs, so a pan or zoom that moves the viewport no
  longer discards tiles that were rendered sharp moments earlier.
- Stop dropping a zoom's tiles when zooming out far enough shows the page
  whole; that made returning to a visited zoom a cold miss, which re-blurred
  the page and re-interpreted it once per tile.
- Bound older-zoom retention by bytes (TILE_BYTES) as well as by count
  (OLD_TILES): a tile of a large scan can be 4096 squared, about 67 MiB, so a
  count alone ran past the tile allowance.
- Add returning_to_a_visited_zoom_reuses_its_cached_tiles, which fails on the
  reverted fix, and cite both view tests in view.deep-zoom-tiles; the entry
  stays partial because the 8192 px-per-side cap is untouched.
2026-10-15 02:54:44 -04:00
89f0fa2bd5 M14: Italian catalog: complete the missing entries (#2320)
* M14: Italian catalog: complete the missing entries

it.tsv lacked 265 keys that the other catalogs carry. The 201 the code
looks up are added in one dated section at the end of the catalog: the
sentences the accessibility checker, preflight, signature validation and
page-range parser word (their WORDINGS), the certificate rows and calendar
weekday and month labels (their tl_ctx contexts), digital-ID, docking and
print messages, and five plurals (one|other). Italian now has 2,528
entries.

The new rows follow the wording the catalog already uses: history labels
say "Modifica ..." like "Change {key}", and the Creator and Producer keys
that label fills in say "Applicazione" and "Produttore PDF", as the
Document Properties dialog names those fields. The Optimize PDF summary
says "eliminato" like its Discard options, the checker and preflight
sentences say "ha rilevato" and "Viene usato", the trust messages say
"nel tuo elenco di certificati attendibili", the password and certificate
rows say "non corrispondono" and "rilasciato" like the existing "The
passwords don't match." and "Issued by" rows, and the print hint quotes
the "Disattivato" option as the dialog shows it.

The other 64 are left out because no non-test code looks them up:
- 10 tooltip texts with a fixed macOS shortcut ("Zoom in (⌘+)"); the code
  fills the shortcut in through "{key}" rows Italian already has;
- 9 older wordings the code no longer uses: the five Combine folder
  messages, "Save (M4)", " on page {label}", the owner-only security
  message and the old "Text recognition isn't installed" message (the
  code now says "its model files are missing");
- 45 generic labels and plurals only the Lithuanian catalog has. The code
  says "{n} file|{n} files" as two plain strings, "1 file" and
  "{n} files", which Italian has; "{n} comment|..." and "{n} result|..."
  are used nowhere.

The entries are AI-assisted translations written from the meaning of the
English (clean-room; no proprietary localisation consulted), and keep
their placeholders and trailing ellipsis. The catalog's SHA-256 in
ATTRIBUTION.toml is updated by hand, as the other catalog changes do.
localization-parity.md, the ROADMAP progress log, the M14 rows of
docs/roadmap.md and docs/localization.md record the new count, and
parity/acrobat-features.toml now says Italian translates the engine's
sentences too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: kcobelli-pixel <257471167+kcobelli-pixel@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:54:32 -04:00
midasmatterhorn 7d7ab3f701 edit: commit the open paragraph when a click switches text boxes (#2302)
Clicking straight into another text box while one was open for typing replaced the editor and
dropped the typed text: only a click on empty canvas (the editor losing focus) or Cmd+Enter
applied it (#2022). The click now queues the open paragraph's edit and asks for the clicked box
to open on the next frame, from the document that edit has been applied to, so the box's index
and geometry are the new inventory's, not the one the click came with. A click on the open box's
own frame keeps the editor and its draft instead of resetting it.

The regression test types into the top of two boxes, clicks straight into the bottom one, and
requires the first box's text applied ("Top edited"), the edit on the undo stack, and the second
box open. On the old code it failed with ["Top", "Bottom"] — the draft had been dropped.
2026-10-15 02:53:35 -04:00
snipeTR 5288946b54 export: zero-pad the page number in Export All Images file names (#2208) (#2295) 2026-10-15 02:53:23 -04:00
Valentin KleinandClaude Sonnet 5.5 cd9e30c3b0 Tabs: keep the drop line in front of the first tab inside the strip (#2225) (#2278)
The line is drawn half a gap left of the first tab, which is outside the
scroll area's clip rect, so it was clipped away. Clamp it into the clip rect.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:53:00 -04:00
Adrián Iglesias ÁlvarezandBrandon Thomas 76c66b8e9c feat(ui): Shift on a corner handle keeps proportions for comments, stamps, and form fields (#2252)
Holding Shift while dragging a corner handle now preserves the annotation's
current proportions, unless the annotation has its own fixed aspect ratio
(image signatures). This matches Acrobat behaviour for comments, stamps,
and form fields.

- Added  helper to compute the effective ratio from fixed
  aspect, current proportions with Shift held, or none.
- Updated  in comments.rs and  in prepare.rs to
  pass the Shift modifier.
- Updated signature_drag.rs to read and pass Shift.
- Added tests for the new Shift behaviour in comments.rs and prepare.rs.
- Updated docs/ui-parity.md and parity/acrobat-features.toml.

Fixes #2199

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:51:55 -04:00
b850cbb5c9 M4: lay out Organize pages as Acrobat's panel (#2248)
* M4: lay out Organize pages as Acrobat's panel

Organize pages gets Acrobat's panel: Pages selected (typed, or by kind
from its menu: all, even, odd, landscape, portrait); page options as a
row of icon buttons (rotate both ways, delete, extract) with Insert (a
blank page, pages from a file, duplicates), Replace, Split and Bates
numbering; page setup (boxes, crop, transitions, templates, printing);
and page labels and properties. Items PdfCraft doesn't have yet are
listed muted and say they are planned.

The panel shows whenever the grid does, however Organize was opened (the
tool, the command, --organize), and its Close ends Organize. The grid's
own toolbar keeps the selection count, moving pages, their size, Save
pages and Close; the page actions moved into the panel with the same
labels, and queue on the view as before. Two new strings are translated
in every catalogue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M4: Pages selected shows the current page when nothing is selected

The empty field showed the example "1, 12, 16-18" in grey, which read as a
selection. It now shows the page the actions apply to, as Acrobat does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(i18n): refresh the catalogue hashes in ATTRIBUTION.toml

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M4: leaving Organize goes back to the workspace's panel

Opening a page from the grid (or the grid's Close, or the panel's Close)
always showed All tools, so Organize opened from the Edit panel dropped
the user out of Edit. It now goes back to the workspace's own panel: Edit
under the Edit tab, Convert's and E-Sign's under theirs, All tools
otherwise. Back still goes to All tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M4: box the Organize panel's page edit (clippy large_enum_variant)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M4: parity notes for the Organize pages panel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: abc2cb <271337822+abc2cb@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:51:32 -04:00
d1d23cc271 fix(organize): draw a rotated page large and centred in its grid cell (#2243)
A page turned a quarter (or any landscape page) was fitted with the
portrait margins and pinned to the top of its cell, so it showed as a
small strip with empty space below. The page picture is now as large as
the cell's picture area allows and centred in it, as Acrobat draws it;
the sides narrow only when a picture that wide would reach the selection
checkbox. Damaged page sizes (zero, NaN, infinite) still give a finite
rectangle inside the cell.

Co-authored-by: abc2cb <271337822+abc2cb@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:51:06 -04:00
68d78c8099 fix(edit): keep Edit text & images boxes drawn while page input waits (#2242)
The boxes round paragraphs and images were drawn by the same call that
takes the page's pointer input, and that call was skipped whenever
something else owned the input: selected objects, added content, or page
input blocked because the window lost focus or a dialog or the palette
was open. Switching to another window made every box vanish until the
pointer came back. The boxes are now drawn whenever the tool is on; only
the input waits.

Co-authored-by: abc2cb <271337822+abc2cb@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:50:55 -04:00
ae9d6246e7 M7: the Edit tab turns on Edit text & images, as in Acrobat (#2241)
* M7: the Edit tab turns on Edit text & images, as in Acrobat

Entering Edit boxes every paragraph and image on the page at once, so text
is editable with one click instead of first finding Edit text & images in
the tool list. The floating toolbar gets an Edit text & images button while
Edit is open or the tool is in use. Leaving Edit (or turning the tool off
there) applies a paragraph still open in the editor, as clicking away from
it does; left open, its page kept showing the paragraph's text removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M7: parity notes for the Edit tab turning on Edit text & images

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M7: the panel-reveal test looks for the Edit panel's own row

With Edit text & images on, its name is on the floating toolbar as well as in the panel, so
the label alone matched twice and no longer showed that the Tools tab was revealed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: abc2cb <271337822+abc2cb@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:50:45 -04:00
Marco Arnulfo 72d6655b28 fix(forms): open a text field on press so dragging over it selects text (fixes #2213) (#2226) 2026-10-15 02:50:13 -04:00
3de8d56784 M3: Save asks before writing over a changed file, one tab per file, locked autosaves (#2166) (#2218)
* M3: Save asks before writing over a changed file, one tab per file, locked autosaves (#2166)

Save compares the file on disk with the version the document came from
(its open, reload or last save) and asks before writing over another
one, with Save as... to keep it; the changed-on-disk notice says what
Reload and Save each do. A file that is open already comes to the front
instead of opening twice. A running instance holds an exclusive lock
file in the recovery folder and names it in its snapshots, so a second
instance neither offers nor discards what the first is autosaving.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz

* M3: the pending save exists only where files do (#2166)

The web build has no file to compare against, so the struct and field
are desktop-only; the wasm check gained no warning.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:49:40 -04:00
RidwanandBrandon Thomas 586f2b2be3 M9: harden .p12 digital IDs (#1847) (#2201)
* M9: harden .p12 digital IDs (#1847)

New digital IDs are written with 600,000 PBKDF2-HMAC-SHA-256 rounds (OWASP)
for both encrypted parts and the MAC, instead of 2048. The MAC gets the same
count because checking it alone tells whether a guessed password is right.

Opening a .p12 now spends at most 10 million rounds of key derivation across
the whole file, charged before any hashing. The file chose its own iteration
counts unchecked (`u64 as u32`), so a crafted file could hang the password
check for about an hour, or wrap 2^32+1 to one round.

Creating an ID needs at least 8 characters (NIST SP 800-63B), from one
constant, pkcs12::MIN_PASSWORD_CHARS, shared by the app, sign_id_create and
its MCP schema. Opening an existing ID takes any password.

* Refresh catalog hashes after merging main

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:48:32 -04:00
3f9cd3b87d M1: every PDF leaves the engine checked, and every writer uses one staging-file writer (#2166) (#2182)
New files from Combine, Extract, Split and Create, signed, timestamped and
LTV-embedded files and the OCR batch output reopen with their pages before
anyone writes them, as saves already did. The desktop app, the automation
tools (with or without --root) and the CLI's edit, combine, extract and
split write through pdfcraft_platform::staging::write_atomically, so a
failed write or --out naming the input never leaves a half-written file.
Special files such as /dev/stdout are still written as given.


Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:48:09 -04:00
c1146d0d8d feat(ui): improve back and forward page navigation (#2080) (#2151)
Co-authored-by: srt8hellcat2-ui <225485253+srt8hellcat2-ui@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:47:33 -04:00
b369a50b68 fix: Space Audit lines up its columns and counts form fonts as fonts (#2118)
* fix(optimize): count what form fields and comments draw with by kind

The space audit claimed the AcroForm and annotations before any page
resources, so the fonts in /DR and in widget and comment appearance
streams were counted as Forms or Comments. A fillable form with an
embedded field font showed mostly "Forms" and almost no "Fonts".

Resources are now sorted by kind first, wherever they are used: page
resources, the form's default resources and every annotation
appearance (one stream or a dictionary of states). A form XObject's
other entries now count as X Object Forms instead of overhead.

Sorting nested forms' resources recursed once per form, so a long
chain of nested forms overflowed the stack. The recursion is now
bounded at 64 levels; deeper forms are still counted as X Object
Forms through the iterative walk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): line up the Space Audit columns and say what it shows

The percentages were drawn at the dialog's far edge, away from their
header and outside the striped rows: egui gives a grid's last column
the rest of the window, and the right-to-left layout used to
right-align the numbers filled all of it. Every cell is now laid out
first and given exactly its column's width; numeric columns and
their headers are right-aligned, and the description column takes
the rest, so the stripes span the dialog.

A caption under the title says the table is what takes up the file's
space now (not what optimizing would save), translated in every
catalogue. The parity notes no longer call the space audit missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:45:58 -04:00
jonathanwxh-cellandClaude Opus 5.5 b655208aaf fix(ui): header, footer and watermark dialogs refuse undrawable text before OK (#2013) (#2107)
The dialog previews in the interface font, which draws Cyrillic, while
applying draws with the standard fonts and refused it after OK.
HeaderFooter::first_undrawable and Watermark::first_undrawable are now
the one check: applying refuses through them, and the dialog shows the
Edit Text warning and disables OK on the same answer.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:44:49 -04:00
3a4de17707 M3: preserve coordinate clicks across slow frames (#2100)
Pair coordinate press and release after hover warmup so frame delays do not turn clicks into holds. Preserve text multi-click selection for coalesced pointer events.

Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:44:14 -04:00
Aleksander HelgakerandClaude Opus 5.5 b0a692c139 fix(ui): check boxes over text show the pointing hand, not the I-beam (#2099)
Hovering a check box or radio button that sits on page text showed the
text-selection I-beam. The form layer set the pointing hand, but on a
plain hover it reported the pointer as not consumed, so the text layer
ran afterwards and replaced the cursor whenever a glyph was under it.
Character sheets hit this on every dot: the boxes are set on printed
"OOOOO", and boxes whose checked look is a font glyph (an "X") count as
text too, since page text includes annotation appearances.

forms_ui::page_input now says whether the pointer is merely over a field
or the field owns the press, and the text layer leaves the cursor alone
while a field is under the pointer. Presses, clicks and drags behave as
before; text fields keep their I-beam.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:43:49 -04:00
712ae9c36a M3: a document that asks to open in Full Screen mode asks first (/PageMode /FullScreen) (#2086)
* M3: a document that asks to open in Full Screen mode asks first (/PageMode /FullScreen)

The Initial View option "Open in Full Screen mode" was written and read
but never acted on when opening. Now the app asks, as Acrobat's alert
does: Full Screen enters it, Not now leaves the window as it is. A
document cannot take the screen on its own.

Refs #379, #1459.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:43:17 -04:00
c0f76fc73a M4: set page transitions and display times (/Trans, /Dur) through the engine and the tools (#2083)
* M4: set page transitions and display times (/Trans, /Dur) through the engine and the tools

organize::set_transition writes or removes a page's /Trans dictionary
(only the entries the style reads and that differ from the defaults) and
/Dur, with every value checked before any page changes; Edit::SetTransition
is one undo step ("Set page transitions", translated in every catalogue);
page_transition_set is the tool. The counterpart of reading them (#2074);
playing them and the Organize dialog stay planned.

Refs #379.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:42:28 -04:00
Lucas Sampaio DiasandBrandon Thomas aa050612c5 M3: preserve Ctrl+Alt+Q input during digital ID creation (#1980) (#2055)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:41:32 -04:00
bbc99126db i18n(es): translate the new print and Add text strings, Type on the signature pad, tú register (#2049)
* i18n(es): translate the new print and Add text strings, Type on the signature pad, tú register

Seven tl! literals reached the Spanish UI in English: the PDF page size,
Match page at 100%, PDF scale and the two scale hints in the print dialog,
and the Done / Discard buttons of Add text. The signature pad's Type
button read "Tipo" (the noun) because Spanish had no `signature pad`
entry; it now reads "Escribir", beside Dibujar, Imagen and Escanear.

Four rows used the formal usted register the catalog header rules out
(Elija, Cámbielo, the signature-photo hint, the no-signature notice);
they now use tú like the rest.

Spanish gains `spanish_covers_ui_literals`, the check Japanese, Italian,
French, German and others already have, and its command test checks
menu names too, so a new string can't ship in English by accident.
`contextual_labels_fall_back_to_the_plain_translation` used Spanish's
missing `signature pad` entry as its example; it now uses a context no
catalog defines, so it keeps testing the fallback as catalogs fill in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:41:10 -04:00
osaym e6e65d0292 fix(ui): wrap long filenames and metadata in Document Properties (#2011) (#2012)
Wrap text in Document Properties rows within the 420 pt content column so documents with long filenames or metadata do not stretch the modal dialog card beyond the viewport boundaries.

Fixes #2011.
2026-10-15 02:38:32 -04:00
8da1d3efce Redaction Tool Properties: the mark's appearance, and Make properties default (#2008)
* Redaction Tool Properties: the mark's appearance, and Make properties default

Acrobat's third group in the Redaction Tool Properties dialog: the mark's
outline colour (/C), a fill colour shown while the pointer is over a mark (a
viewing preference, never written), and the mark's opacity (/CA). Make
properties default: ticked, OK keeps the values for the next run (saved with
the settings as redact_prefs); unticked, they hold for this run only and the
saved defaults stay; Cancel puts back what the dialog opened with. The dialog
is laid out in two columns so it fits a laptop screen. Rows in every
language, their checksums in ATTRIBUTION.toml, parity
protect.redaction-properties shipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Osvaldo Duran <338149330+regendorf-ux@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:38:22 -04:00
6c22c78a72 Edit: crop an existing page image with its handles (#2002)
* Edit: crop an existing page image with its handles

ImageChange::Crop(rect) keeps the part of a page image inside rect (user space)
with a clipping path placed just inside the scope that holds the image (or a
new q … Q around its Do); a clipping path already at the scope's start, the
picture's earlier crop, is replaced, so a crop can grow back. The pixels are
never scaled, and a later move or resize wraps the scope, so the crop moves
with the picture. ImageEdit::Crop (undo label "Crop image"), image_edit
action crop. In the window, right-click ▸ Crop Image puts the selected
picture in crop mode: eight handles trim (the inside pans), the part that goes
is dimmed, Enter or Done applies, Esc or Cancel leaves; Remove Crop shows the
whole picture again. Rows in every language, their checksums in
ATTRIBUTION.toml, parity edit.image-crop shipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Image crop: Bulgarian hint without a character the monospace UI font lacks

JetBrains Mono has no 'ѝ', so primary_ui_fonts_cover_latin_and_cyrillic_catalogs
failed; the hint is reworded ("with its handles" -> "with the handles").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Osvaldo Duran <338149330+regendorf-ux@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:37:56 -04:00
Valentin KleinandClaude Sonnet 5.5 77640a030f Draw: a released pen stroke stays on screen until the page raster has it (#1858) (#1987)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:37:18 -04:00
bb2b03f263 View: Single Page wheel turns to the next page at the edge of a zoomed-in page (#1924) (#1986)
* View: in Single Page view the wheel turns the page at the edge of a zoomed-in page (#1924)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* ROADMAP: log edge paging (#1924)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* View: no jump past the last/first page, a turned page starts at its edge (#1924)

step_page is a no-op at either end (it re-aligned the view via goto), the page-view
ScrollArea is not animated (its zero-length offset target overwrote the turn's jump), and
the rest of a gesture that turned the page no longer pans the new page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:37:07 -04:00
Valentin KleinandClaude Sonnet 5.5 16829991fe Viewer: a press on a page makes it the current page (#1956) (#1985)
Page commands with no selection (Rotate, Delete) act on the current page. A page gone to
earlier stayed current while it showed whole, so the next press on the page beside it
rotated the wrong one.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:36:45 -04:00
75c9a9c27b Comments: font size for text boxes in Comment Properties (#1908) (#1984)
* Comments: font size for text boxes in Comment Properties (#1908)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:36:35 -04:00
Valentin KleinandClaude Sonnet 5.5 76037a77ac View: show page size when hovering the bottom-left of the page view (#1855) (#1979)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:35:26 -04:00