Commit Graph
4058 Commits
Author SHA1 Message Date
aiongg 733e38bfa7 fix(cos): a decode budget shared by every stream one walk reads
MAX_DECODED bounds one decode, so a page whose /Contents repeats a
reference to one compressed stream, or a form drawn many times, gets a
fresh 1 GiB allowance per occurrence. DecodeBudget is one cumulative
decoded-byte allowance plus a form-entry count and a seen-set of stream
references, shared by a whole page walk. A decode that runs past the
remaining allowance spends all of it.
2026-10-22 16:57:54 +00:00
Combiler 634aed8036 Merge branch 'main' into main 2026-10-22 16:55:12 +00:00
aiongg 693eae1d56 docs(parity): cite the audit's new tests and record its bounds and copy restriction 2026-10-22 16:49:00 +00:00
aiongg 3fce90e94a fix(audit): withhold covered text when copying is not permitted
The audit returned the text under each cover whatever the document's
permissions, and the CLI and the automation tool printed it. A user
whose password withholds copying could read through the audit the text
that text extraction refuses them. The audit now reports every finding
for such a document with empty covered text; the owner password lifts
the restriction as it does elsewhere.
2026-10-22 16:44:38 +00:00
aiongg 65ee18d470 fix(audit): bound cover-line comparisons and covered text
Every cover was compared with every text line of its page, and each
finding kept its own copy of the text it covered. Both grow with the
square of the content: a few megabytes of covers over one long line
meant hours of comparisons and gigabytes of report. The audit now
counts comparisons per page and covered-text bytes across the document
and fails with an error past either bound, never with a partial
report.
2026-10-22 16:44:38 +00:00
aiongg 3e306c22ef fix(audit): compare a cover with the start of a merged text line
A text line joins compatible runs on one baseline, and the audit took
the line's last painting operator as its paint position. Text drawn
after a cover on the same baseline joined the covered words and moved
the line past the cover, so the covered words went unreported. The
audit now compares against the line's first painting operator: any
run painted before the cover is checked, and runs painted after it
in the same low-contrast color are unreadable on it too.
2026-10-22 16:44:38 +00:00
aiongg 4c7ba85beb fix(audit): resolve an indirect /Subtype before skipping an object
The annotation scan and the form XObject walk read /Subtype only as a
direct name. A /Subtype stored as a reference to the name read as
none, so a /Redact annotation or a covering form named that way was
skipped as an unrelated object and the page reported clean. Both now
follow the reference, and a reference that cannot be loaded fails the
audit.
2026-10-22 16:44:38 +00:00
aiongg cc1086a862 fix(audit): keep the pending path across Q
The cover walk cleared the current path when it restored the graphics
state, but the current path is not part of the graphics state. A
rectangle built before Q and filled after it paints a box over the
text, and the audit missed it and reported the page clean.
2026-10-22 16:44:37 +00:00
Janiz-zzzandClaude Opus 5.5 667bc697b2 i18n(fi): translate the 64 labels left in English after #2210
Replaces the English placeholders added during the #2210 integration
(calculation order, text formatting, print, thumbnails, layers, comment
reshape and signature checks, plus the two signature wordings from
3e180944) with Finnish, and updates the fi.tsv sha256 and the entry
count in docs/localization.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:38:44 +03:00
Oliver HagmannandClaude Opus 5.5 9a76f90f07 M5: the arrow keys move the selected comment
With a comment selected, the arrow keys scrolled the view (↑ ↓) or turned the page (← →), so
a text box, rectangle or line could only be placed with the mouse. Now an arrow key moves the
selected comment a screen pixel, and ⌘/Ctrl or Shift and an arrow ten, as Edit text nudges
objects; up is up on screen whatever the zoom, the page's rotation and the view's. Each press
is one undo step (Edit::MoveAnnotation). The keys are taken before the view's navigation,
which has them back once nothing is selected. Text markup stays with its text, and a locked
comment where it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:33:32 +02:00
HiteshandClaude Opus 5.5 7d6af8a508 fix: Delete removes the selected paragraph or objects in Edit text
A paragraph selected by its frame (or objects taken by an area selection) answered the
arrow keys and Escape but not Delete, so the only way to remove text was one typed
character at a time in the inline editor. Delete or Backspace now removes the whole
selection as one undo step: paragraphs and images last-first in their inventories, added
content after them, under the existing "Delete content" history label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:56:38 +05:30
Hamza 19f40e6554 The two file tests ask which file, not how the OS says it is missing
Review of #3849: both tests asserted `stderr.contains("No such file")`, which
is the Unix wording. On Windows the same error reads "The system cannot find
the file specified. (os error 2)", so `test (windows-latest)` failed on them.

Neither test is about the wording. What they are about:

- `a_flag_that_takes_no_value_does_not_swallow_the_file`: the error is about
  `missing.pdf`, and is not "check-one: missing file" -- the file reached the
  command instead of being eaten as `--edit`'s operand.
- `a_file_whose_name_starts_with_dashes_is_still_reachable`: the error names the
  file the command could not open (`missing.pdf`, or `./--dash.pdf` for the
  `info` case), and is not a usage error about a value or an unknown option.

Both now assert exactly that, plus the absence of our own usage phrases. Every
string they match is one this CLI prints, so the OS has no say in it.
2026-10-22 21:21:00 +05:00
alehel 154ffb65b1 Merge remote-tracking branch 'origin/main' into fix/background-hidden-by-page-backdrop
# Conflicts:
#	ATTRIBUTION.toml
2026-10-22 18:19:06 +02:00
alehel efe2518385 Merge remote-tracking branch 'origin/main' into feat/all-tools-expand-view
# Conflicts:
#	ATTRIBUTION.toml
2026-10-22 18:18:48 +02:00
aiongg d5047aa4ac test(cos): build the private temp directory without an unused mut on Windows
The only mutable use of the DirBuilder is the Unix mode call, so the binding
is mutable only where that call exists.
2026-10-22 12:13:16 -04:00
aiongg 7cdde6c91c chore(assets): record the changed translation catalogues in ATTRIBUTION.toml 2026-10-22 12:13:16 -04:00
aiongg 7c4731234f style(sign): rustfmt the new tests 2026-10-22 12:13:16 -04:00
aiongg 3e18094426 i18n(ui): carry the two new signature wordings in the catalogues that must cover them
Czech and Vietnamese get translations; Catalan, Finnish, Lithuanian,
Brazilian Portuguese and Romanian carry the English until translated.
2026-10-22 12:13:16 -04:00
aiongg 4e9d8f9b3b fix(sign): require the time-stamping purpose for trusted timestamps
A timestamp counted as trusted time whenever its signer's certificate
chained to a trusted certificate, whatever the certificate was issued
for. Any key from a trusted CA could then vouch for an arbitrary time.
Trusted time, for a document timestamp and for a signature's embedded
one, now also needs a certificate whose extended key usage includes
timeStamping, whose key usage allows signing, and which has no critical
extension PdfCraft doesn't process (RFC 3161 §2.3). The test timestamp
authority gets such a certificate.
2026-10-22 12:13:16 -04:00
aiongg d9cb7c2fe7 fix(sign): don't build chains through a CA with unprocessed critical extensions
Chain building accepted an intermediate CA without looking at its critical
extensions, which were checked only on the signer's certificate. A
subordinate CA limited by critical name constraints could vouch for a
signer outside its namespace, because PdfCraft does not enforce those
constraints. A CA certificate (other than a self-signed root, whose
limits are the trust store's business) with a critical extension
PdfCraft doesn't process is refused as an issuer, and the reason is
reported.
2026-10-22 12:13:16 -04:00
aiongg fbada004d3 fix(sign): treat a changed trailer /Root after signing as not permitted
Revision checks compared objects only. A later revision that pointed the
trailer at another catalog already present in the signed file changed no
object, so the signature stayed valid while the reader showed a different
document. A /Root that differs from the signed revision's is now a
disallowed change to the document structure.
2026-10-22 12:13:16 -04:00
aiongg faab42d405 fix(sign): authorize an embedded OCSP responder by signature, not by name
An OCSP response's signer counted as the issuer itself whenever its
certificate carried the issuer's subject name. A response naming its
responder by key hash could embed a certificate for any key with the
issuer's name copied in, skip the delegation checks, and mark a valid
certificate revoked. Whether the responder is the issuer now follows from
how it was selected (the issuer's own name or key hash); every embedded
certificate must be signed by the issuer and carry the OCSP-signing
purpose.
2026-10-22 12:13:16 -04:00
aiongg b72c730a95 fix(cos): give the protect example a fresh key on every run
The example passed a constant seed to set_encryption. AES-256 derives the
file key from the seed alone, so anyone could rebuild the key and read a
file it protected without either password. The seed now comes from the
operating system's randomness through RandomState, as in the engine.
2026-10-22 12:13:16 -04:00
aiongg 7fb6ab0f1f test(cos): write the qpdf oracle files into a private new directory
The qpdf oracle test wrote into temp_dir()/pdfcraft-enc-<pid>, reusing the
directory if it existed and following any symlink inside it, so another
local account could prepare it and have the test overwrite a file of the
developer's. The directory now has an unpredictable name, is created by
the test (mode 0700 on Unix) and the files are created new.
2026-10-22 12:13:16 -04:00
aiongg 60d2d1e3f2 fix(cos): put inherited crypt-filter selectors under the new protection
A stream that named its own crypt filter (/Filter /Crypt with
/DecodeParms /Name) kept that name when the document was given new
password protection. The new /Encrypt defines only /StdCF, so the
inherited name resolved to /Identity and the stream was written in
clear into a file the user asked to protect. When saving with protection
applied by set_encryption, a selector the new handler does not define is
removed with its parameters and the stream follows the new policy.
Saving with the original handler keeps the source's selectors.
2026-10-22 12:13:16 -04:00
aiongg a08d974ac1 fix(cos): encrypt the /Contents of an annotation that carries a /ByteRange
Saving with encryption left a dictionary's /Contents in clear whenever it
had a string /Contents and an array /ByteRange, whatever else it was. A
source file could add a /ByteRange to an ordinary annotation and its
comment text was then written unencrypted. A dictionary with a /Subtype or
a /Type other than Sig or DocTimeStamp is never a signature dictionary, so
its /Contents is encrypted like any other string.
2026-10-22 12:13:16 -04:00
alehel 5db4480b40 Merge remote-tracking branch 'origin/main' into fix/background-hidden-by-page-backdrop
# Conflicts:
#	parity/acrobat-features.toml
2026-10-22 18:04:34 +02:00
alehelandClaude Opus 5.5 ff2962c2f4 fix(ui): test that Properties ▸ Space Audit measures again after an edit
The tab caches the audit per document and edit generation. Pin both
halves: frames with the file unchanged keep the same rows, and an edit
made while the tab is open (a new bookmark) shows up on the next frame.
With the cache keyed on the document alone, the test fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:58:11 +02:00
Lombardi2003andClaude Opus 5.5 01744fd7eb feat(organize): Extract pages takes typed page ranges (#3593)
Extract pages only took the selected pages. Its dialog now has a Pages
field for ranges such as "1-5, 8, 11-13", read like Combine files' and
Print's ranges, in the order typed and each page once. Left empty (as
the dialog opens), it takes the selection as before, shown as the hint.
A range it can't use says why and turns Extract off; Extract and delete
removes the typed pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:50:52 +02:00
Osvaldo DuranandClaude Sonnet 5.5 f84d6b17e6 Merge upstream main 95263631 into pr/attachments
Merged cleanly, no conflicts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 15:48:35 +00:00
alehelandClaude Opus 5.5 16d2b2ef50 Merge origin/main into fix/document-properties-layout
Conflict in parity/acrobat-features.toml: main moved the integration
tests into tests/it/, so the evidence paths take main's side and this
branch's two dialog_chrome tests are added on the new paths; the notes
keep this branch's wording. The custom-properties entry is now partial
through the tools, but the dialog still has no Custom tab, so the
"No Custom tab" note stays true.

Also fixes a break the merge could not show as a conflict: the cursor
test's form.pdf fixture is ../data/form.pdf from tests/it/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:47:21 +02:00
Adrien AlbertiandClaude 1240c95592 M12: keep the 1:N field in step with the scale fields; review fixes
- ratio_text is derived from the scale fields (one function, sync_ratio_text):
  on page seeding and after a hand edit of drawing points, real distance, unit
  or page unit it shows the matching 1:N, or is emptied when the fields are not
  a plain m/mm drawing scale. The "Common scales" highlight follows it.
- format_ratio no longer rounds (1:1.234, 1:0.001), so the field shows exactly
  the ratio that applies.
- The smallest accepted ratio is 1:0.001, keeping the real distance within the
  minimum of its field; set_ratio refuses out-of-range ratios too.
- "Scale", "Common scales", "Enter a scale such as 1:50." and a hint that
  applying a scale switches units to m / mm are in every i18n catalog, with a
  completeness test.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-22 17:43:59 +02:00
mtlcl410andClaude Fable 5.1 7e94c24532 M3: the theme button test starts on System (#1248)
The click-cycle test assumed a fresh install starts Light; it starts on
System now, so the cycle is System, Light, Dark.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 23:41:18 +08:00
mtlcl410andClaude Fable 5.1 62c595ceba M3: a fresh install follows the desktop's appearance (#1248)
The theme preference defaults to System: dark on a dark desktop, light
where the desktop says nothing. Saved settings and --theme still win.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 23:41:18 +08:00
Combiler01 112b34b305 fix(ui-egui): adjust include_bytes path in editing test after tests/it migration 2026-10-22 21:11:16 +05:30
Max AbramowitzandClaude Opus 5.5 795fb53c5d M3: the community card on Home can be closed for the rest of the run
The ArtCraft community card on Home gets a close button. Closing it hides
the card until the next launch; the choice is deliberately not remembered,
and the same links stay in the top bar's Discord button and the Help menu.
A UI regression covers closing and the fresh-launch default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:37:39 -04:00
alehel 845642691f Merge remote-tracking branch 'origin/main' into feat/all-tools-expand-view 2026-10-22 17:32:21 +02:00
Eric Kaden 9206174761 Merge remote-tracking branch 'upstream/main' into fix/xfa-layout-drops-usage-rights
# Conflicts:
#	parity/acrobat-features.toml
2026-10-22 11:31:44 -04:00
Eric Kaden e646bb8684 Merge remote-tracking branch 'upstream/main' into fix/exec-menu-attachments
# Conflicts:
#	parity/acrobat-features.toml
2026-10-22 11:31:44 -04:00
Eric Kaden a29dd8b94e Merge remote-tracking branch 'upstream/main' into fix/util-scand
# Conflicts:
#	parity/acrobat-features.toml
2026-10-22 11:31:44 -04:00
Combiler01 7d2b02633c Merge remote-tracking branch 'upstream/main' 2026-10-22 21:01:36 +05:30
Combiler01 df6783032b fix(edit): extract image to its own tag when arranging shared marked-content sequence 2026-10-22 21:01:28 +05:30
alehelandClaude Opus 5.5 fd971559e7 M3: going Home puts the expanded All tools away with no document open
With no document open the expanded view covers Home itself, so the control
option `home` changed no tab and the view stayed up (CodeRabbit's remaining
merge-risk note on 8a8bb92d). The option now puts the view away itself.
`going_home_with_no_document_puts_the_expanded_all_tools_away` was seen to
fail with the fix reverted.

Also doc comments on the view's `show` and `tile` and the sidebar's
`all_tools`, for CodeRabbit's docstring-coverage check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:29:03 +02:00
aiongg 9526363152 Build each crate's integration tests as one binary (#4087)
* Build each crate's integration tests as one binary

Every tests/*.rs file is its own executable that links the whole crate
graph. Move each crate's test files into tests/it/ as modules of one
tests/it/main.rs, for apps/pdfcraft, apps/pdfcraft-cli, automation, cos,
crypt, engine, render, sign and ui-egui. ui-egui's ui_group binary and
the support modules move into tests/it/ too. Crates with a single test
file keep it. The test executables go from 150 to 52, and every crate
runs the same number of tests as before.

ui-egui perf keeps its own binary (own process): its frame-time budgets
would charge each measurement for the other tests running beside it.

What the move needs:
- include_str!/include_bytes! and #[path] paths gain one "../".
- ui-egui: one GPU lock in tests/it/main.rs replaces the five per-file
  ones (content, document_links, editing, fill_sign, view), so one
  process creates one wgpu device at a time. The drop-line test in ui.rs
  and the screenshot blocks behind PDFCRAFT_*_SHOT(S) and
  CRAFT_UI_DOCKING_DIR, which render without holding it, take it too.
- pdfcraft winit_wayland_scroll: the vendored winit source it compiles
  names crate::event, so that shim lives in main.rs.
- parity evidence names the new paths.

* Name the one-binary test paths in docs and comments

- READMEs, docs, ATTRIBUTION and source comments name tests/it/<file>.rs.
- The oracle README's example filters with --test it encryption::qpdf_agrees.
- pdfcraft's GPU lock comment points at the UI tests' shared lock in
  crates/ui-egui/tests/it/main.rs.
2026-10-22 11:28:58 -04:00
Eric Kaden 7a9c3b28c7 Merge remote-tracking branch 'upstream/main' into fix/util-scand 2026-10-22 11:04:00 -04:00
alehelandClaude Opus 5.5 8a8bb92d33 M3: choosing a tool or another tab puts the expanded All tools away
The expanded view kept covering the workspace after a control client chose a
tool (`tool=organize`) or went Home (`home`), so the chosen panel or the Home
view never showed. The same held for a document tab click, Open Recent on an
open file, and the Combine files tab.

select_tool now clears the view, so every way of choosing a tool does. The
view also remembers which tab was in front when it opened, and another tab
coming forward puts it away, whichever path brought it there.

Each new test was seen to fail with the fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:00:41 +02:00
alehelandClaude Opus 5.5 cb21b2cce4 M3: opening a file and expanding All tools take the usual workspace path
Review follow-ups for the expanded All tools view:

- Opening a document left the view on, either over the new document (All
  tools as the default workspace) or waiting to reappear on the next switch
  back (Read as the default, or a mode override). Activating a document now
  puts it away, and the reset lives in enter_mode, which every workspace
  change goes through.
- The sidebar's expand button and `--tools full` assigned the mode directly.
  From Read the right panel saved on entry was not restored, and from Edit the
  Edit text tool stayed active. Both now go through switch_mode, as the mode
  bar does, so the sidebar shows the All tools list again on collapse.

Each new test was seen to fail with the fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:47:14 +02:00
Combiler01 3552193703 Modifed Test to run on Flaky Cl Test 2026-10-22 19:20:44 +05:30
Ridzzz0Alam b98d8a2819 M6: form_fill also warns about lines too tall or too wide for their box (#4018)
Two cases still drew a value cut off without saying so. A single-line
value at a fixed font size taller than the box (12 pt text in an 8 pt
field) was checked only for width, so it fit sideways and was cut at the
top and bottom. In a multiline field, a character wider than the box
becomes a line of its own, which fits vertically but is cut at the sides.

The single-line and comb branch now compares the line's ascent to
descent against the clip box (a new `Overflow::High`), and the multiline
branch reports the widest wrapped line that is still wider than the box.
An empty value draws nothing, so it is never reported, however small the
box.

Fixes #4018
2026-10-22 15:47:54 +02:00
jonathanwxh-cell 17f84e4391 Merge main into feat/optimize-compatibility-version
Retain compatibility regression coverage with upstream content-boundary and image-size tests.
2026-10-22 21:34:02 +08:00