Commit Graph
4844 Commits
Author SHA1 Message Date
PerchAnton 0910b8314a M12: retain product notes while integrating current main 2026-10-18 18:07:15 +03:00
wstock e336b48a35 cos: a rebuild prefers a trailer whose /Root is the catalog (#2365 review)
A later cross-reference stream (or trailer) whose /Root points at another
dictionary, such as /Info, replaced an earlier good trailer, and a full save
then wrote that dictionary as /Root. A candidate whose /Root passes the
root_is_catalog rule (/Type /Catalog or /Pages) now beats one whose /Root is
only some dictionary, wherever it is in the file. The looser rule still
applies when nothing better is there, which FDF files (whose /Root has
neither key) and the repair path for a non-catalog /Root rely on.

Test: reconstruction_skips_a_trailer_whose_root_is_not_the_catalog
2026-10-18 15:06:31 +00:00
mtlcl410andClaude Opus 5.5 5486100d04 chore: refresh catalogue hashes after rebasing onto main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-18 23:03:36 +08:00
mtlcl410andClaude Opus 5.5 6be953459c fix(print): the options read ends at its deadline and a dead read wakes the dialog (#1818)
Review follow-up on #2108:
- output_within waited on the reader thread with join, so a process the
  command started could hold the output pipe, and the wait, long past the
  limit after the command exited or was killed. The output now comes back
  over a channel with recv_timeout; such a reader finishes on its own.
- the printer-list and options workers ask for a frame from a guard
  dropped on exit, also when the source panics, so an idle dialog sees
  the closed channel without waiting for another event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-18 23:03:31 +08:00
mtlcl410andClaude Opus 5.5 b59b91f0be fix(print): Vietnamese for the printer-options loading note (#1818)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-18 23:03:31 +08:00
mtlcl410andClaude Fable 5.1 e3847ba076 fix(print): pass the lpoptions limit outside the stand-in command in the CUPS test
The limit landed inside `command(...)` in one Unix-only test, which this
Windows machine never compiles; checked now with a Linux target.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-18 23:03:31 +08:00
mtlcl410andClaude Fable 5.1 7ec435eb4d fix(print): Properties… reads CUPS driver options off the GUI thread, with a deadline for lpoptions (#1818)
The Print dialog's Properties… ran lpoptions on the GUI thread with no
deadline, so a CUPS that stopped answering froze the window. The read
now runs on its own thread (the panel says so meanwhile, and a read
that dies offers nothing rather than waiting), and lpoptions gets ten
seconds through spool::output_within, which kills and reaps a command
at its deadline; the PPD's own defaults then stand. One UI string in
the 21 catalogues.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-18 23:03:31 +08:00
Marco Arnulfo 6ee8329ec0 test(ui): the reduced-copy regression saves a real PDF and reopens its text (#2150) 2026-10-18 17:03:20 +02:00
PerchAnton 4f4f589365 M12: retain product notes while integrating current main 2026-10-18 18:02:45 +03:00
mtlcl410andClaude Opus 5.5 024407992d style: cargo fmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-18 22:56:57 +08:00
aiongg 40d7b0bc47 Merge upstream main a12fe8f41 into next 2026-10-18 14:56:01 +00:00
Marco Arnulfo b913c2851d fix(ui): a second file dialog waits for the open one instead of being refused (fixes #2150) 2026-10-18 16:53:23 +02:00
PerchAnton 42a31db308 M7: retain product notes while integrating current main 2026-10-18 17:51:48 +03:00
aiongg eedf250b4c Merge upstream main ecffce69e into next
crates/cos/src/document.rs: both new accessors, header_offset (#2525) and reconstructed (#2029).
parity/acrobat-features.toml: the malformed-xref entry has both sides' evidence and notes (#2029 full save of a repaired file, #2717 table numbered from 1).
2026-10-18 14:50:20 +00:00
a12fe8f411 M4: custom document properties through the engine and the tools (#2076)
* M4: custom document properties through the engine and the tools (#organize.custom-properties)

/Info entries beyond the ones the spec names are custom properties:
organize lists them (custom_info) and set_info accepts any such name
after Acrobat's checks (no standard name, no empty name, no spaces,
delimiters, / or #, at most 127 bytes); inspection lists them too
(DocInfo::custom); doc_info reports them under custom and doc_set_info
sets or clears them. The Custom tab in Document Properties and the XMP
mirror stay planned.

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

* M4: custom document properties list the same before and after an edit

Review follow-up. The open-time reader (render/inspect.rs) now uses the
engine's rule (pdfcraft_organize::custom_info, trimmed, empty values
dropped): text strings only, NUL/whitespace trimmed, empty values left out,
and the 256 cap counting listed entries. Before, it also listed name
values (`/Dept /RnD`) and capped before filtering, so the list could change
after the first unrelated edit. An automation test opens a file with a name
value, blank, NUL-padded, empty and numeric entries and checks doc_info's
custom list is the same before and after setting the Title.

Drops the ROADMAP.md log line, which conflicted with main.

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

* M4: custom properties drop NUL/whitespace padding in one pass, before the cap

Review follow-up. custom_info now trims NULs and whitespace in any mix
(" \0Lab\0 " is "Lab"), leaves out values that are only padding, and does
both before taking 256, so blank entries can't crowd real ones out. It is
the one rule: the engine uses it as it is, and the open-time reader trims
the same way. Tests: 300 blank entries before a real one, and mixed
padding, in organize and through doc_info.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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-18 10:49:30 -04:00
Dylan M. Taylor 9a7824557e docs: use generic office terms and clarify fidelity harness and parity tracking reference 2026-10-18 10:49:21 -04:00
Dylan M. Taylor feb6c38580 docs: drop references to competing commercial software 2026-10-18 10:48:33 -04:00
mtlcl410andClaude Opus 5.5 ecffce69ee fix(engine): Save writes a repaired file in full even without edits (#1937) (#2029)
* fix(engine): Save writes a repaired file in full even without edits (#1937)

A damaged file is repaired on open (its cross-reference index rebuilt from
object headers, as Document Properties reports), but Save wrote it back as it
was: `Session::save_bytes` returned the bytes as opened whenever nothing was
edited, and a repair is not an edit. The "repaired" file on disk was still the
damaged one.

`save_bytes` now writes a reconstructed document in full even with no edits.
`Document::reconstructed()` in cos names the condition (no revision chain:
rebuilt, or built from scratch), and `write_incremental` uses it too, so the
writer and the engine share one definition.

Tests: `engine::tests::a_repaired_file_is_rewritten_by_save_even_without_edits`
(table offsets shifted; no table at all) and
`automation::saving_a_repaired_file_puts_the_repair_on_disk` (doc_open,
doc_info, doc_save over the same path, reopen). Both fail with the condition
removed: the saved bytes equal the damaged file. parity: core.repair-broken-files
cites them and notes that a repair still does not count as an unsaved change.

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

* fix(engine): a signed repaired file keeps its bytes on Save without edits (#1937)

Review follow-up: writing a reconstructed file in full on Save puts the
repair on disk, but for a signed file it would also invalidate its
signatures without warning. Save now skips the early return only when the
document is reconstructed and not signed. A regression test signs a
document, appends a `startxref` that points nowhere (the signed bytes stay
intact, the cross-reference data must be rebuilt) and checks that Save
without edits returns those bytes.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 10:47:28 -04:00
jonathanwxh-cell 87e68246c8 style(render): rustfmt CropBox condition (#2838) 2026-10-18 22:43:01 +08:00
PerchAnton 57c5dde167 M7: preserve rotation and index regressions on current main 2026-10-18 17:39:44 +03:00
jonathanwxh-cell c6bf3d0dbd fix(render): avoid emitting full ActualText replacement when span is partially cropped (#2838) 2026-10-18 22:39:15 +08:00
alehelandClaude Opus 5.5 23ed4cca65 M3: All tools view keeps Escape for dialogs, leaves with the mode option, and searches Arabic
Review follow-ups for the expanded All tools view:

- Escape closed the view even when a dialog or prompt over it owned the key.
  It now checks modal_open() first.
- set_option("mode", …) changed workspace without clearing the view, so
  leaving and coming back to All tools reopened it. Another workspace now puts
  it away, as switch_mode does.
- Right-to-left translations are stored in display order, so a multi-word
  Arabic query typed in logical order missed its tool. Such a query now matches
  word by word, as the command palette's search does.

Each test was seen to fail with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:33:12 +02:00
PerchAnton e45a1db89f M1: reject partial resource-fork copies and integrate current main 2026-10-18 17:30:26 +03:00
alehelandClaude Opus 5.5 eca16e6bd5 fix(edit): backdrop copies keep the stream dictionary and every slot
Review of #2815 (CodeRabbit):

- A content stream whose bytes live in an external file (`/F`) draws
  those, not its inline bytes, so it is never taken for a backdrop.
- The copy keeps the original stream dictionary, unknown entries
  included, less `Length` and `DL` (Flate re-encodes it); it had started
  from an empty dictionary.
- Every `/Contents` slot that lists the original stream gets the copy: a
  page listing the same stream twice painted the white fill again over
  the background.
- The Background dialog starts with replacing on each time it opens.
  Update restores backdrops before re-applying, so a replaced page shows
  no fill and no checkbox, and a choice left off from an earlier dialog
  hid the background again unseen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:24:14 +02:00
aiongg b6b02a1b27 Merge upstream main 1952a8880 into next
ROADMAP.md, docs/roadmap.md: union of both sides' lines, each once.
parity/acrobat-features.toml: the XFA entry has both sides' evidence and notes (#2604 page areas, #2813 CLI layout).
2026-10-18 14:22:30 +00:00
jonathanwxh-cell b2727ac15b M2: refresh footnote reference line selection against current main (#2960) 2026-10-18 22:22:19 +08:00
jonathanwxh-cell 76d1bea652 M2: refresh CropBox text clipping against current main (#2838) 2026-10-18 22:17:35 +08:00
alehel 522c1dcd22 Merge remote-tracking branch 'origin/main' into feat/all-tools-expand-view
# Conflicts:
#	ATTRIBUTION.toml
2026-10-18 16:17:28 +02:00
PerchAnton 6425df5038 M12: reconcile current accessibility and XFA roadmap status 2026-10-18 17:15:06 +03:00
jonathanwxh-cell 6c611edc61 M2: refresh clip-mode text extraction against current main (#3116) 2026-10-18 22:14:54 +08:00
PerchAnton 02fe96c528 M12: reconcile current accessibility and XFA roadmap status 2026-10-18 17:14:42 +03:00
PerchAnton d743760624 M12: reconcile current accessibility and XFA roadmap status 2026-10-18 17:14:18 +03:00
mtlcl410andClaude Opus 5.5 4899072089 fix(forms): read dynamicRender only from the configuration packet itself (#3180)
Review follow-up: in a whole-XDP stream, a config/acrobat/acrobat7/
dynamicRender path inside xfa:datasets/xfa:data came before the real
config packet and decided the form type. The lookup now follows direct
children only: the config child of the xdp root (or the config packet
itself), then acrobat, acrobat7 and dynamicRender.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-18 22:14:02 +08:00
PerchAnton 2493253821 M12: reconcile current accessibility and XFA roadmap status 2026-10-18 17:13:59 +03:00
alehel 911147126c Merge remote-tracking branch 'origin/main' into fix/background-hidden-by-page-backdrop
# Conflicts:
#	ATTRIBUTION.toml
2026-10-18 16:05:19 +02:00
Ozt-y 4945677717 fix(content): restrict '>EI' terminator detection to ASCIIHex filter 2026-10-18 19:32:09 +05:30
jonathanwxh-cell ff5a2fc9eb M2: retain clipping text in filtered extraction (#3116) 2026-10-18 22:00:21 +08:00
jonathanwxh-cell 3c94700447 M11: document partial PDF version compatibility (#3227) 2026-10-18 21:54:58 +08:00
1952a88801 fix(cli): use laid-out pages for dynamic XFA forms (#2813)
Fixes #1645. For a dynamic XFA form, `info` reported the placeholder
page and field counts, and `text` and `render` read the placeholder
pages, while the app and the automation tools lay the template out.

The CLI now opens dynamic XFA forms through the engine session and uses
its laid-out document for page and field metadata, text and pixels.
`info` keeps the input's file size and also reports the original counts
as `pdf_pages` and `acroform_fields`; `text` checks the copy permission
before extracting. The input file is never written.

Checks run (rustc 1.99.0): cargo fmt --check; cargo clippy -p
pdfcraft-cli --all-targets -- -D warnings; cargo test -p pdfcraft-cli;
cargo xtask layers; cargo xtask assets.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:54:00 -04:00
6525140955 M3: type an exact zoom into the rail's zoom field (#1755)
* M3: type an exact zoom into the rail's zoom field

The rail's zoom percentage was a read-only label. It is now a text field:
Enter applies a typed percentage (150, 150%, 62.5 %), Escape or unparsable
input keeps the current zoom. The control channel's zoom option uses the
same parser.

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

* M3: document parse_zoom_percent

* i18n: Vietnamese for the zoom field's tooltip

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:53:24 -04:00
Marco ArnulfoandBrandon Thomas b68b3c1119 M3: the Pages panel follows the current page (#2785)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:52:40 -04:00
2c4989413f M7: the Edit text caret is as tall as the row it is on (#2462)
In a region that mixes sizes (a 24 pt title over 12 pt lines), the
inline editor's caret on a small row was as tall as the region's first
size and reached into the row below. egui draws the caret at least one
row of the TextEdit's font tall, and that font had the paragraph's first
size. Each row already carries its source row's height, so the
TextEdit's font is now half the region's smallest size and only sets
that floor.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:51:52 -04:00
Andrew BarnesandBrandon Thomas 6ce3bc198b fix(gpu): request adapter-supported device limits (#2383) (#2433)
Clamp the surface device descriptor to the adapter's capabilities while
preserving its maximum 2D texture extent. Cover V3D shader variables,
lower GL capabilities, alignment ordering and capable-device defaults.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:50:42 -04:00
Ian HollowayandBrandon Thomas 1581cd1edd M10: keep the OCR scan fixture at its original page size (#2399)
Assisted-by: GPT-6

Signed-off-by: IanHollow <72767437+IanHollow@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:50:16 -04:00
a50ddd4de1 fix(i18n): zh-hant catalog uses Taiwan vocabulary consistently (#2856)
Several entries carried mainland-Chinese computing terms in a catalog
written in the vocabulary used in Taiwan:

- 丟失 → 遺失 (unsaved-changes warning)
- 郵政編碼 → 郵遞區號 (Zip Code field formats)
- 自簽名 → 自我簽署 (self-signed digital IDs, three entries)
- 灰度 → 灰階 (Grayscale Images, Print in grayscale; the rest of the
  file already used 灰階)
- 匹配 → 相符 (ten search/match entries; 符合 stays for the
  fit/conform sense: 符合頁面, 符合 PDF/A)

Translation column only — source keys, placeholders and ellipses
unchanged. ATTRIBUTION.toml hash re-pinned.

Generated with [Devin](https://devin.ai)

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:49:36 -04:00
2eb1639097 Find credit card numbers followed by an expiry, year or CVV (#2969)
* Find credit card numbers followed by an expiry, year or CVV

Found while testing crates/redact/src/patterns.rs:180-190 (credit_card, via digit_run at :53)

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

* Redact: a card followed by a number drops at most four trailing digits

A longer grouped number (an IBAN's digits) no longer matches on a Luhn-valid prefix; the run is
read far enough to see the tail and refused when it goes on past that.

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:49:13 -04:00
MarekSpodBiedryandBrandon Thomas 25495b959b Keep a subscript or superscript in its line when extracting text (#3174) (#3244)
* Keep a subscript or superscript in its line when extracting text (#3174)

* Text extraction: only a glyph right beside the previous one gets the sub/superscript centre limit

A large negative gap (a small glyph drawn back under a large one) kept the wider limit.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 09:48:49 -04:00
mtlcl410andClaude Opus 5.5 2bd6d1357d M5: flattening comments leaves 3D, multimedia and print-production annotations (#3087) (#3089)
doc_flatten with comments chose every non-widget annotation except Link
and Popup, so a comments-only flatten baked in and deleted 3D, Screen,
Movie, RichMedia, PrinterMark, TrapNet and Watermark annotations with
their data (a 3D annotation's U3D model and default view). The comments
branch now uses pdfcraft_annot::is_comment_subtype, the classifier the
comment list uses; widgets follow `fields` only.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:48:26 -04:00
mtlcl410andClaude Opus 5.5 f0c275669a M7: never encode typed text with CID 0 (#3223) (#3235)
Word's Type 0 subsets map CID 0 (.notdef) as well as the real space glyph
to U+0020 in ToUnicode and list a width for CID 0. encode keeps the lowest
code for each character, so an edited line wrote every space as <0000> and
drew a box where each space was. CID 0 is .notdef whatever ToUnicode says,
so has_glyph now refuses it and the space takes the real CID.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:48:14 -04:00
NetterSilverandClaude Haiku 5.5 7e3f413f52 Redact email addresses whose local part starts with punctuation (#2948)
Found while testing crates/redact/src/patterns.rs:141-146 (email)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-18 09:48:02 -04:00