* 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>
every_registered_command_is_implemented skips a hand-kept list of commands
that open file pickers. file.pin_folder and create.multiple_separate are not
on it, so every run of the ui-egui tests showed two real file dialogs on the
developer's desktop (seen through the xdg portal on Linux). The test now
answers any picker as a cancel through pick_override.
* M4: keep the last saved revision across undo (#1310, #1311, #1312)
Undo treated a return to the opened file as unsaved work, and after a save it followed the restored snapshot's older base. Revert and the next save now stay on the last saved file, so that revision remains in the PDF.
* 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.
* Drop the unused batch fixture left by the merge
Main already keeps the written PDF in original, so the older source binding is unread and fails -D warnings.
Reorder document tabs by dragging while keeping the active document and its view state. Add Ctrl+Tab and Ctrl+Shift+Tab cycling in visible order, including Combine files, and translate the menu labels.
Keep timestamped batch test expectations tied to the originally generated PDF bytes.
Closes#1537Closes#1542
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Carry selected tool actions through native file preparation, password retries and conversion, and defer startup intake until pending native opens finish.
Await observable completion in native opening, password, recovery and linked-PDF tests while retaining their content, identity, dirty-state, cancellation and destination checks. Verify the expected validation error and preserve the original document after refused consent targets.
M3; refs #1914.
* feat: presentation (.pptx) export (#1137)
- Implement OpenXML PresentationML (.pptx) generator in crates/export/src/pptx.rs:
- Generate [Content_Types].xml, _rels/.rels, core properties, presentation.xml
- Build slide master, blank slide layout, and DrawingML theme
- Render slides per page: largest text block as title shape, remaining paragraphs as body text box with font size, bold, italic, and color formatting
- Embed images with proper DrawingML picture shapes and relationships
- Generate DrawingML table shapes with column grid and span merging
- Wire OfficeFormat::Pptx into crates/engine/src/compare.rs and export_office
- Register export.pptx command and mark Presentation (.pptx) Ready in catalog
- Support pptx in automation doc_export_office tool
- Wire export.pptx into UI commands and file save dialog
- Add unit and integration tests across crates/export, engine, automation, and ui-egui
- Update create.export-pptx parity feature evidence and docs
* pptx export: saturating slide positions, matching cell-border tags, theme ea/cs fonts, package test
- Heights and the running vertical position use clamped/saturating arithmetic, so a hostile
text size or image rectangle (1e300, NaN, inf) can't overflow i64; the item loop uses get().
- The table cells' a:lnR and a:lnT borders were closed with </a:lnL>, which made every slide
with a table malformed XML; they now close with their own tags.
- The theme's major/minor fonts carry the schema-required a:ea and a:cs children.
- New test walks the produced zip entry by entry (sizes, inflation, central directory), checks
every XML part's tags balance, the required parts, content-type overrides, relationship
targets and that no placeholders are used; a hostile-sizes test covers the overflow.
- Test literals gain main's Block/Image rotation and size fields.
- Parity notes say what is still unverified.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: KrzysztofC <krzysztofc@localhost-live.lan>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A PDF protected with only a permissions (owner) password opens without
asking, restricted, and nothing let the user enter that password
afterwards: the security notice only led to a read-only Security tab and
the hint said to reopen the file with the password, which the app had no
way to do (#995, #1077).
Now the security notice and Document Properties > Security offer Unlock...,
and Change settings / Remove security on a restricted document ask for the
permissions password first and then carry on, as Acrobat does. The open
password is refused there.
- crypt: SecurityHandler::authenticate_owner accepts only the owner
password and refuses a different file key.
- cos: Document::unlock upgrades the open document in place; owner and user
passwords unlock the same file key, so nothing is decoded again.
- engine: Session::unlock (not an edit: no undo step, not marked unsaved)
also unlocks the undo and redo states and reopens saves as owner;
Document::can_unlock, EditError::WrongPassword, command protect.unlock.
- ui: the permissions password prompt shares the open prompt's modal; the
notice and Security tab get Unlock...; the disabled-command hints no
longer point at reopening the file.
- i18n: the new strings in all 15 full catalogs (catalog hashes updated).
Tests cover crypt, cos and engine (all four algorithms, wrong and open
passwords, undo, redo, save, removing security afterwards) and the UI
(notice flow, Remove security asking first).
Closes#995Closes#1077
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The Save button's tooltip and accessibility name read "Save (M4)", a
leftover milestone tag, while Print and Document properties beside it
name their shortcuts. It now uses command_tip with file.save, so it reads
"Save (Ctrl+S)" on Windows/Linux and "Save (⌘S)" on macOS. The catalog
key becomes "Save ({key})" in the 15 catalogs that had it (ATTRIBUTION.toml
hashes updated). New test commands::the_top_bar_save_button_names_its_shortcut_not_a_milestone.
Claude-Session: https://claude.ai/code/session_01UngnNXzwMCF7ksQ8tNucDe
Co-authored-by: DWAA1660 <DWAA1660@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Clicking a tool in the tool panel or palette that needs a document, with
none open, used to show only "Open a document first". It now shows the
open picker (titled with the tool's name), opens the chosen file and runs
the tool on it. Encrypted files run the tool once the password is in;
cancelling runs nothing. Menus and shortcuts keep the old message. The web
build opens the file only.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
View ▸ Zoom and Page navigation were hand-written with "⌘1", "⌘[" … while
the registered items below them were formatted by Shortcut::label, so
Linux showed both ⌘1 and Ctrl+K in one menu. Ten tooltips and the view-key
rows of Help ▸ Keyboard shortcuts hard-coded ⌘ the same way.
- The keys the document view handles itself are Shortcut constants in
ui-egui commands.rs, written with the same Shortcut::label.
- Labels follow egui's detected OS (ctx.os()) instead of cfg!, so the web
build in a Linux or Windows browser shows Ctrl too, and tests can check
both platforms.
- Translated tooltips take a {key} placeholder (13 catalogs; only the
shortcut token changed, hashes updated in ATTRIBUTION.toml).
- Fit visible is listed once in View (in Zoom), not also in the generated
list; Ctrl+3 and the palette still run it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* tests: pin the interface language in the harnesses added since #502#502 pinned it in every harness there was. These five arrived after its
branch point, so the suite is red again on a machine whose display
language has a catalog:
- `commands.rs`, Clear Recent Files from the menu (#557).
- `editing.rs`, `session_harness` (#462), which clicks "Don't save".
- `stamps.rs`, the rotated-stamp test (#473), which clicks "OK".
- `ui.rs`, removing one recent file from Home (#557), and the narrow tab
strip (#416), which reads "Display theme:".
The eight other `PdfCraftApp::new()` without the line never draw the
shell, so `i18n::set_current` is never called and the thread-local stays
on English. They compare saved state or a default, and are left alone —
including the one that asserts on a `notify_tr` toast, which reads
English for the same reason.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* tests: pin the interface language in the date-format and Pages-panel harnesses too
---------
Co-authored-by: Fernando Areán <soporte@codebit.es>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The ui-egui integration tests drive the shell by its English labels, and
`i18n::system_lang` returns `Lang::EN` under `cfg!(test)` so they can. That
only covers the unit tests: an integration test links the library as it
ships, where `cfg!(test)` is off, so `PdfCraftApp::new()` leaves `language`
on `auto` and the shell is drawn in the system language. On a Spanish
Windows eleven of the test binaries fail; a French, Russian, Chinese,
Japanese, Portuguese, Czech or Telugu machine does the same. CI never sees
it because its locale is English.
`tests/theme.rs` and `tests/create.rs` already pin the option; the other
119 harnesses now do too, and the comment in `system_lang` says why, so a
test copied from an existing one keeps the line.
Also `i18n::tests::windows_display_language_query_returns_a_locale_tag`
treated the return of `windows_ui_language()` as a single tag. It is the
preferred-language list, one tag per line, as `first_supported` reads it,
so on a Windows with two or more display languages the assertion tripped
over the newline. It now checks the shape of every line.
Co-authored-by: Fernando Areán <soporte@codebit.es>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* 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>
* 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>
- The wheel turns pages in single-page view: one per notch, one per swipe.
- The rail's Page display menu offers Acrobat's view choices.
- Preferences: default page display and default zoom.
- Fit page and Fit height no longer jump across mixed page sizes.
* M12: add calibrated 2D measurement tools
* M12: harden measurement tools for landing (#208)
Bounded snap targets (endpoints only for accepted segments, page-wide cap),
lenient extraction (deep q nesting and undecodable streams are skipped and
reported), per-page snap cache, measure::list reports unsupported entries
instead of failing, closed polygons with a repeated vertex, printable unit
labels (m²), cached panel listing, WinAnsi captions, indirect /VP kept,
measure.export skipped in the command test like other pickers, missing
translations, honest parity (scale and distance partial) and ROADMAP.
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>
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now
PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the
PrintCraft settings and crash-recovery folders to the new name, and both apps read settings
saved under the old storage key, so upgrades keep recent files, preferences and unsaved work.
The signing test fixtures keep their PrintCraft subject names (they are binary).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Both were marked shipped on the generic 'every registered command is
implemented' test, which the honest assessment flagged. Full screen: the
shortcut enters, Escape leaves, and the command toggles both ways. About:
the dialog shows the running version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>