eframe stores the window's size and place just before App::save. A
minimized window has none: Windows reports 0 x 0 at (-32000, -32000),
and the next start opened at egui's 64-point floor in the screen's
corner (seen on a 6K display at 225%: a 144 x 206 px window). While the
window is minimized the app now puts back the last settings it was
shown with (or none), and a window that opens at that floor from
settings saved before this fix is given a usable size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
The first fix dropped a stale pick while painting the next frame. The window only redraws on
the next input, so after deleting a picture in Edit text & images its highlight stayed on screen
until the user clicked. Clear the pick in apply_edit, in the same step as the edit, and ask for a
repaint. drop_stale stays for the paths that change the document without apply_edit (Undo, Redo).
An edit-level test checks the pick right after apply_edit with no frame in between; it fails
without the change. The earlier one-frame test could not tell, the test harness redraws more
often than the app does.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Review of #2625: the test checked the argument reveal_command builds but
not how it is passed, so it still passed with raw_arg replaced by args.
The process is now built by reveal_process, and a Windows test runs it
on `cmd /c echo`, which prints its command line back: with Rust's own
quoting the argument comes back wrapped in quotes with the inner ones
escaped, and the test fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
With the Select tool a click picks a picture and draws a blue highlight over it. The pick was
only cleared by Escape, a drag or another click. Picking a picture, then entering Edit text &
images and deleting it left the highlight on the empty spot, with nothing left to click to
remove it.
The pick now remembers the document's edit generation and is dropped once the document has been
edited since (a delete, a move, Undo), the same condition under which the copy shortcut already
refused it as stale.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Read mode hides the tools, not the navigation panes, as Acrobat's does
and as switch_mode already says, but the floating quick toolbar was
still drawn over the page there. It is now hidden in Read mode, and
entering Read mode puts the current tool down (back to Select), so no
markup tool stays active without a toolbar to show it. The right rail,
the page navigation and the V/H shortcuts are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): validated signature display, snapshot tab reuse
Signature widgets with layered appearances baked the unverified state
into the page, so a valid signature kept reading 'Signature Not Verified'
while the banner and panel said valid. Validated verdicts are now drawn
into the icon and caption layers in the display copy only: the signed
bytes are untouched, saving keeps the original appearance, and files
without those layers or without a valid verdict render exactly as before.
Other valid signatures still get the verdict mark over their widget,
sharing one style with the Signatures panel, and hide it again once the
appearance itself shows the verdict.
View signed version and View revision opened a new tab on every click,
and every tab showed the document title, so repeats piled up identical
tabs. Repeats now reuse the snapshot tab, snapshot tabs read like their
source plus the suffix in the tab strip, window title and Combine picker,
and carry a notice bar naming the revision with a Close that asks about
saving first.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Address review comments on validated appearances and trust refresh
Count every field when checking shared appearance streams, so a
caption shared with an unvalidated field is never rewritten for the
valid one alone.
Pick the icon slot by drawing instead of length, and only when a
single layer qualifies. Bare rectangles never count as the icon, and
ambiguous or missing slots fall back to the overlay badge.
Revalidate and invalidate together through one helper, so trusting a
certificate always drops rendered pages. Trust alone still marks
nothing dirty.
Tests: mixed-verdict sharing, longer decorative borders, competing
and doubled layers, capture-before preservation, trust invalidation
without zooming, and signed Session round trips with rendering,
saving and passwords.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Prove the icon slot geometrically and cover real layered signatures
A lone line drawing is still not the icon: frames and borders span the
box while the status mark lives in a corner of it. The icon now has to
sit strictly inside the appearance frame and cover at most half of it;
anything else falls back to the overlay badge. Widgets without a
measurable frame veto the rewrite the same way.
Tests: parsed extents and inhabitant checks as units, both reported
border repros plus a frameless widget, and real layered signatures
built with cms::sign_detached through Session open, trust, rendering,
save and reopen, plain and password-protected.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Identify the icon by shape instead of geometry
A lone line drawing is still not the icon, but size and position were
the wrong signals: the real status mark is large, and geometry blocked
it. Frames, borders and panels are rectangles by nature while the mark
is not, so a drawing made only of axis-aligned rectangles is decoration
however it is stroked. Singularity, shared-stream and template checks
are unchanged; anything ambiguous still falls back to the overlay badge.
Tests: rectangularity as a unit, nested line-drawn frames, and a
triangular fixture mark in place of the rectangular one.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Budget the appearance walk and keep the marks with the bytes
The walk that finds a signature's icon and caption capped depth,
layers and fields, but not the work: a dictionary whose entries all
point at the next one is visited once per entry per level, so a small
hostile file never finishes opening. The walk now counts its visits
and gives up, and a field whose tree cannot be read to the end stops
the whole pass, because a layer we never read may be the one two
fields share and a rewritten shared layer puts one field's verdict on
another field's page. The sharing count is exact again with that: two
widgets of one field pointing at the same appearance claim it once,
or the field vetoes its own rewrite.
A mark used to outlive the bytes it described. A failed display write
left the page showing the file's own caption while the verdict still
claimed its state was drawn; a metadata edit dropped the mark while
the page kept drawing the state, so the badge landed on top of it. The
write fallback now drops the marks and logs the loss, and metadata
carries them over.
Tests: a self-referential appearance tree, which never finished before
the budget, two widgets sharing one appearance, a metadata edit that
keeps its mark, a save that goes through the writer instead of
short-circuiting on the opened file, and the snapshot bar's Close
button pressed for real.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Cite the new regressions in parity, restore main's installer note
The appearance entry now cites the bounded walk and the save through
the writer, and says what a tree that cannot be read to the end does.
The installer entry carried main's older text, which still claims
signed installers are planned; main's newer note is taken verbatim.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: preserve rounded decoration beside raster status glyphs
* M9: place appearance progress beside signature history
* M9: retain appearances with ambiguous curved layers
* M9: keep signature verdicts in viewer overlays
Defer generic appearance rewriting: caption text and a unique drawing do not establish an icon role. Preserve original artwork for saves, image and text exports, and both print modes while retaining snapshot reuse and trust refresh. Cover chamfered borders and mixed signer/caption streams through signed Session round trips.
---------
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
The find-step shortcuts sat after the canvas's "egui wants keyboard
input" check, so they did nothing while the find field had the focus
(Enter worked). They now run before it, while a search is open, as in
Acrobat and Preview.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
The bar beside selected text closed after Highlight, Underline, Strikethrough,
Add comment and Replace text because those consume the selection. Copy kept the
selection, so the bar stayed open. Remember the copied selection and hide the
bar until the selection changes; the words stay selected for ⌘C or another
action.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explorer reads /select,"<path>". The path went in as one ordinary
argument, which Rust quotes whole when it contains a space
("/select,C:\a b.pdf"), and Explorer answers that by opening Documents.
A file saved to a folder or name with a space (often after Save As) was
therefore never shown. reveal_command now quotes the path after the
comma and the Windows runner passes the arguments as written; a path
containing a quote, which Windows doesn't allow, is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
The jump scrolls to MARGIN - GAP above the page. A page that fits has
nothing to scroll, and egui clamps the offset only after drawing the
frame, so the page showed 14 pt too high for a frame and then hopped
down. Clamp the jump to the scroll range before handing it to egui.
At Fit Page in single-page view, every wheel turn forward drew one
blank frame before the new page appeared.
- Lay out the shown row again after the wheel is read. The jump to the
new page was measured from the old row's place and scrolled both out
of view; going back it came out negative and was clamped, so only
forward turns flickered. The edge-turn deferral that worked around
this goes.
- Prefetch the rows either side of the shown one, so the next page is
rendered before the turn instead of showing blank paper.
* Fix main UI regressions in paging, fonts and test expectations
* Wait for asynchronous dropped-document preparation in regression
---------
Co-authored-by: Codex <codex@openai.com>
the Revert prompt to zh-hant.tsv; merged together, the catalog held both
copies and `bundled_catalogs_are_consistent` (and the tests that load
zh-hant as a reference) failed on main. Keep #1750's wording (字型大小,
「」 quotes, as the rest of the catalog) and drop the other copy.
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Comments panel: page groups fold from their chevron (fixes#1554)
* Comments panel: translate the fold-all buttons in every catalog
"Collapse all pages" and "Expand all pages" were missing from the 21
catalogs, so the covers_ui_literals tests failed; the catalogs' hashes
are re-pinned in ATTRIBUTION.toml.
* Add the Vietnamese entries for Collapse/Expand all pages; refresh catalog hashes
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>
* M14: discover installed UI fallback fonts through Fontconfig
Find existing fallback faces in configured directories, including Nix store paths. Bound and cache discovery while preserving candidate names, glyph checks and the screenshot opt-out.
Assisted-by: OpenAI Codex
* M14: keep the drop-dispatch regression deterministic
Select inline document preparation in the existing drop-dispatch test, matching the validation fix used by both Wayland branches. Full workspace, strict Clippy and wasm gates pass.
Assisted-by: OpenAI Codex
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Complete the Arabic (العربية) catalog: every missing row translated
ar.tsv covered 2,325 of the interface rows; the newer catalogs (lt, cs,
es and friends) had since grown to a 2,578-row union. This adds the 253
rows Arabic was missing, in the same clean-room Modern Standard Arabic
and Acrobat-style vocabulary the file already uses:
- digital-signature verification detail: every ECDSA/RSA/BER/DER reading
note, timestamp-token and revocation sentence, the certificate
inspector's sixteen fields (المُصدر، الرقم التسلسلي، صالح من/إلى…)
and the key-usage purposes (عدم الجحود، تشفير المفاتيح…)
- the accessibility checker's full vocabulary (وسوم، بنية المستند،
النص البديل، خلايا الرأس…) and its report grades
- the eight newer @plural rows with all six Arabic forms (صور محسّنة،
بكسلات، نتائج…) and the calendar's weekday abbreviations
- PDF/A output intents, combine/organize status lines, panel restore
errors, and the small chrome (اختيار…، تنظيم، تقرير مفصل…).
Same catalog, same lookups — Arabic now covers the union of every
language's rows (2,578).
* Refresh the ar.tsv sha256 in ATTRIBUTION.toml
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Refresh catalog hashes after merging main
---------
Co-authored-by: hasan-sdaif <306196098+hasan-sdaif@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M6: make list boxes directly selectable and scroll to selection (#1524)
- In-place selection: List boxes are rendered directly on the page and
selectable in place. Clicking an option row selects it immediately;
Ctrl/Cmd-clicking toggles items in multi-select lists, and Shift-clicking
selects ranges, without opening a popup menu.
- Appearance and scroll (/TI): When options are selected, /TI (top index)
is automatically adjusted so the selected items are scrolled into view
within the widget's visible rows, and option rows and selection highlights
are drawn into the appearance stream.
- Unit and UI tests: Added tests in pdfcraft-forms verifying /TI scrolling
and appearance generation for multi-select lists, and UI tests in
pdfcraft-ui-egui covering in-place click and Ctrl-click toggling.
Also ensured test harness in reload.rs explicitly sets language to en.
Fixes#1524
* List boxes: keep the engine's page appearance, consume arrow keys
Drop the overpaint of every fillable list box with an egui mock (it ignored the
PDF's fonts, colours and /MK /R; the engine redraws the appearance with the new
/TI), and consume ArrowUp/ArrowDown in the list overlay so they don't also
scroll the page.
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>
* M7: grow text boxes and edit existing text
Text boxes now grow to the page edge and vertically as needed, retaining their size when saved. Clicking native or added text in either text tool edits the existing item instead of layering a duplicate over it.\n\nCo-authored-by: OpenAI gpt-6.1-sol <noreply@openai.com>
* #1539: build against main's Document::added() accessor and draft-aware prepare
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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
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>
* 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>
* 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>
* 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>
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>
* 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>
Under a full workspace test run the background find could still be searching after 60 short
frames: 12 of 15 matches were in when the assertion ran. The PAGE re-run had the same race.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* 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>
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.
Fixesstorytold/pdfcraft#2338
Claude-Session: https://claude.ai/code/session_01DsT1y8xBvkXeBxEUQqdDtj
Co-authored-by: Claude <noreply@anthropic.com>
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.
* 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>
* 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>
Four ui_group harnesses waited at most one wall-clock second (200 x 5 ms)
for the page to render, and stopped as soon as nothing was pending, which
can be before the text layer is even asked for. Under load the text
layer came later and column-select, identity and update-check tests
failed. They now wait through support::wait_until for what they use (a
glyph on screen, the highlight in the document), with its 30-second
deadline. The highlighter quick-click test of #1277 itself did not fail
on this base in 256 runs.
Claude-Session: https://claude.ai/code/session_01PCdPYhYkggLJVYoME2kr5R
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- 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.
* 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>
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.