Commit Graph
4058 Commits
Author SHA1 Message Date
oracola91 628f43e17a fix(ui): the recovery folder is owner-only on Unix (#3320)
Recovery snapshots are unsaved copies of open documents. The folder was
made with the default mode, so on a shared Linux or macOS machine other
accounts could list it and read the snapshots of documents that are not
encrypted. The folder is now made owner-only (0o700), and a folder an
earlier version made is tightened when an instance starts or writes a
snapshot, as image_pick does for its folder. A volume that can't change
modes (FAT, some network shares) works as before, with one warning in
the log.
2026-10-18 17:07:17 -04:00
mtlcl410andClaude Opus 5.5 6b099bef72 docs: vendor/README keeps one hayro-interpret row, with the patches lost to number collisions (#3312)
Merges left four hayro-interpret rows; the current one lacked the
transparency-group, Type 3 rendering-mode and Lab white-point patches (each
numbered 25, 27 or 28 on a branch where another patch took that number).
They are now (41), (42) and (43) in the one row, with their tests.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:07:05 -04:00
104411dc7a Read doubled-sign integers like --280 in the COS lexer (#3299)
* Read doubled-sign integers like --280 in the COS lexer

Fixes #3111

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

* COS lexer: strip the doubled sign without allocating

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

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 17:06:53 -04:00
Morten Husted Lochmann f280ca8e6d M10: exported TIFF and JPEG pages record the resolution they were drawn at (#3332)
* M10: exported TIFF and JPEG pages record the resolution they were drawn at

A page exported as TIFF said 1 x 1 with no unit, whatever the dpi, and
a JPEG export said nothing, so other programs showed and printed them at
a default (a 300-dpi page shown at 72 dpi is four times too big).
encode_image now takes the dpi: TIFF X/YResolution in inches, the JFIF
density in dots per inch.

PNG stays without pHYs. It counts pixels per metre, so 72 dpi would read
back as 72.009, and a page made from an exported PNG (doc_create from
images, as enhance_camera_image_through_tools does) would come out 0.01%
small.

Exporter records the resolution a page was actually drawn at (dpi_used),
so a page too large for the renderer says the lower one. render records
--dpi, since it refuses oversized pages. The TIFF is written with the
tiff crate, already in the graph, because image's TiffEncoder can't set
a resolution.

* M10: TIFF page exports are LZW-compressed, as documented

encode_tiff's comment said "LZW-compressed by the encoder's default",
but the TIFF was written uncompressed: a Letter page at 300 dpi came to
25 MB. It is now LZW without a predictor, the compression TIFF readers
support most widely after none.

* Contributors: add my name for the About window
2026-10-18 17:06:41 -04:00
58795e9188 cli: info names the standard a document declares (#3328)
* cli: info names the standard a document declares

A PDF/A file was summarised with no mention of PDF/A. `info` prints two dozen
fields -- tagged, xfa, javascript, layers -- and said nothing about the
standard, while the app's Standards panel reads the same declaration from the
catalog's XMP and shows "PDF/A-2b" (#3319).

`info` gains `pdfa` (the part and conformance together, e.g. "2B"), `pdfua` and
`output_intents`, from `pdfcraft_preflight::declared` -- the call the Standards
panel makes, so the two agree by construction rather than by a second reading
of the metadata. A document that declares nothing leaves them null and empty.

Read in the CLI rather than in `pdfcraft_render::Inspect`: render is layer 3
and preflight layer 4, so the summary struct can't reach it, while an app may
depend on any layer.

* cli: read the declaration from the bytes already in hand

`Document::open_with_password` takes an `Arc<Vec<u8>>` and `info` already holds
one, so `bytes.to_vec()` copied the whole file for every call -- a file-sized
allocation on a large PDF, for nothing. Clone the `Arc` instead.

* info: name the declared PDF/A level as the automation tools do (PDF/A-2b)

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:06:29 -04:00
NetterSilverandClaude Haiku 5.5 8d6254aa0b Render weekday letters in FormCalc date pictures (#3321)
Found while testing crates/js/src/formcalc.rs:1929-1957 (format_date), with DateFmt returning "EEEE, MMMM D, YYYY" at :2333

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-18 17:06:18 -04:00
NetterSilverandClaude Haiku 5.5 666648349b Drop U+FFFE and U+FFFF from XFDF/XML form-data export (#3330)
Found while testing crates/xfdf/src/lib.rs:192-209 (esc), used by export_xfdf (:445) and export_data XML (:594-612)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-18 17:06:07 -04:00
dependabot[bot] 0c9c621a1f ci(deps): bump cachix/install-nix-action (#3322)
Bumps the actions group with 1 update: [cachix/install-nix-action](https://github.com/cachix/install-nix-action).


Updates `cachix/install-nix-action` from 31.11.1 to 31.11.2
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md)
- [Commits](https://github.com/cachix/install-nix-action/compare/13d8dd58da0234aa297dedd986986ccb8e7f3e24...448cde739abec1eaf0646249e3d25bb8d9bc7702)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-version: 31.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-18 17:05:55 -04:00
Ahmad MuandBrandon Thomas 38999b3e90 fix(edit): isolate added marks from unfinished marked content (#3279)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-18 17:05:44 -04:00
Perchenko Anton 6fc17441ac fix(forms): edit button labels in field properties (#3064)
* M6: edit push-button labels in field properties (#2892)

* M6: keep roadmap status blockquotes valid

* M6: keep button-caption roadmap notes minimal
2026-10-18 17:04:49 -04:00
cyfung1031 acdd8d251c M2: copy Flate back-references in runs (#2035)
* M2: copy Flate back-references in runs

* M2: fold Flate note into hayro-syntax row
2026-10-18 17:03:46 -04:00
jonathanwxh-cell 2512b49f80 test(render): derive leading-superscript selection range via Text::nearest (#2960) 2026-10-19 04:59:52 +08:00
Hamza 645a8ca205 Render: a five-point polyline is no longer painted as its bounding box
`path_as_rect` decided a path was a rectangle from its element count and from
the four corners of its bounding box being covered. Its comment names the shape
it means to match -- one MoveTo, three LineTo, one ClosePath -- but `len() != 5`
also admits one MoveTo and four LineTo, a five-point open polyline, and
covering the corners says nothing about the points between them. Both call
sites then paint `device.draw_rect(bbox)`, so such a path was drawn as its own
bounding box.

Rendered through `pdfcraft-cli render`, an open zigzag whose ends reach the
corners of its bounding box

  80 38 m  80 21.75 l  60.571 29.875 l  40 21.75 l  40 38 l  S

lost its middle peak and gained a straight edge the file does not contain: no
dark pixel near the peak, and 36 along the bounding box's top. A six-point
zigzag and a real rectangle both drew correctly, which is why this shows on
some art and not the rest -- and `s` escapes it because its ClosePath makes six
elements.

A point that is not a corner now rejects the path. A rectangle still matches
whether written `m l l l` and closed or `m l l l l` back to its first corner,
since every one of its points is a corner. The same mistake also discarded a
clip of that shape, through `push_clip_path`, which this fixes too.

Found from craft-libs' ATTRIBUTION note that the shared reader centralizes the
patched Hayro trees from PdfCraft: the same flaw is in craft-libs, sent there
as storytold/craft-libs#175.
2026-10-19 00:29:13 +05:00
Florent Siegenthaler cb7ad709a7 i18n(fr): drop the rewording of existing rows
Undo the effect of bd740ee9: every row that main already has is back to
main's text, the rows this branch adds keep the wording of the first commit,
and the header note recording the exception is removed. The catalog stays
clean-room as its header says.
2026-10-18 21:22:03 +02:00
mtlcl410andClaude Opus 5.5 3ef3c9efa9 M8: tag cleanup refuses what it can't finish instead of removing the tree (#3302)
Maintainer review on #3285: an element written in place that owns redacted
marked content or tags a removed annotation, an inherited page the lookup gave
up on (over 256 ancestors or a loop), and a walk past its limit are now
RedactError::Tags, so nothing is changed. Removing only /StructTreeRoot left
outline /SE and GoTo /SD reaching the elements. Inherited pages are cached per
ancestor. clear_every_element and drop_tree are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-19 03:20:34 +08:00
Florent Siegenthaler a1fc2550c7 Merge branch 'main' into fr-complete-catalog
Resolve the conflict in crates/ui-egui/src/i18n/fr.tsv: main's new rows are
kept as they are and the branch adds only the keys main lacks.
2026-10-18 21:18:12 +02:00
majidninja df591ff88d Rail: scroll the panel buttons above the page navigation in short windows (#3282)
The rail laid its panel buttons out top-down and the page navigation
bottom-up in the same space, so in a window too short for both they were
drawn on top of each other, with the page number field over the icons.

The navigation cluster is now its own bottom panel and the panel buttons
sit in a vertical scroll area in the height left above it.
2026-10-18 19:13:19 +00:00
Dylan M. Taylor a8cd797a91 ci: configure Dependabot for Cargo and GitHub Actions (#3304)
* ci: configure Dependabot for Cargo and GitHub Actions

* ci: add Cargo update entry for /crates/filters/fuzz in Dependabot

* ci: consolidate Cargo Dependabot directories in pdfcraft
2026-10-18 15:11:14 -04:00
qwerinandClaude Opus 5.5 b4bfe2b2b5 M14: complete and review the Czech catalog
Czech had every `tl!` literal but lacked ~220 keys the other catalogs
carry: the sentences the engine words (signature details, accessibility
findings and report, PDF/A problems, pages to print, history labels),
the date picker's weekday heads, the certificate viewer's fields and
shared buttons. All are now translated (2,674 entries).

"Tag" is "tag" throughout, as the catalog's own glossary says: the Tags
panel said "značky", which also names redaction marks, and "{n} tags"
read "1 značek".

Two new tests pin the engine wordings (with their holes), weekdays and
certificate fields; both fail on the previous catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:02:25 +02:00
Osvaldo DuranandClaude Opus 5.5 f24a26b951 Merge upstream main 932bf9c6 into pr/attachments
Conflicts: ATTRIBUTION.toml, crates/organize/src/tests.rs (code resolved by hand: crates/organize/src/tests.rs). Catalogs keep upstream's rows, then this branch's rows upstream lacks; ATTRIBUTION.toml is upstream's with every sha256 recomputed; progress lines keep both sides, upstream's first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 14:59:30 -04:00
oracola91 687425fa7f ATTRIBUTION.toml: new SHA-256 for the es, lt and vi catalogs
The catalogs this PR changes are tracked assets, so their recorded
hashes follow the edits (cargo xtask assets).
2026-10-18 21:52:37 +03:00
Marco Arnulfo 9e0937c3f7 M12: the Visual filter only hides shading, since visual differences are never listed: say so 2026-10-18 20:38:18 +02:00
Marco Arnulfo 947f0f2994 XFA: turn TIFF pictures by their Orientation tag 2026-10-18 20:37:33 +02:00
jonathanwxh-cell 9b0aeb0f69 M11: integrate main b0e5c6eb into PR #3227 and fix version parser lifetime 2026-10-19 02:35:26 +08:00
oracola91 262ae579f8 i18n: Lithuanian and Vietnamese for the new signature messages
The Lithuanian and Vietnamese catalogs came to main after this branch.
Their tests require every key the other catalogs have, so the four new
signature messages need a line in each.
2026-10-18 21:32:13 +03:00
oracola91 954c5142e7 fix(ui): the message bar shows a signature problem before "Certified by"
With a valid certification signature, the message bar said "Certified
by ..." whatever the other signatures said, so an approval signature
added later that is invalid, or whose validity is unknown, did not show
in the bar. The Signatures panel listed it correctly.

The bar now says "Certified by ..." only when every signature is valid.
Otherwise it shows "At least one signature is invalid." or "At least one
signature has problems.", as it already did for documents without a
certification.
2026-10-18 21:30:52 +03:00
PerchAnton 2448074def M4: assert redo survives destination-budget refusal 2026-10-18 21:25:45 +03:00
oracola91 343f1b05cc fix(sign): signed attributes must carry the message digest and the data content type
With signed attributes, the signature value is made over the attributes,
and their messageDigest is what ties it to the document. The digest was
compared only when the attribute was present, and the content-type
attribute was never checked. RFC 5652 section 5.3 requires both.

A signature whose signed attributes have no messageDigest, or name a
content type other than id-data, is now invalid. A missing content type
is reported and the signature is read anyway. A signature that still
validates with an RSA key under 2048 bits now gets a note, as SHA-1 and
RIPEMD-160 do. The four new sentences are in WORDINGS and in Spanish.
2026-10-18 21:19:46 +03:00
PerchAnton 031532b8a5 M4: bound named-destination pruning during page deletion (#3309) 2026-10-18 21:01:43 +03:00
NetterSilverandClaude Haiku 5.5 57c6e476db Keep text rows in other layers, gs states or Tz out of a paragraph
Fixes #3115

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 18:00:00 +00:00
mtlcl410andClaude Opus 5.5 ecb990c8ce M10: glyph names: a uni part with a surrogate group maps to nothing; numbered names read under a suffix (#3170)
Review follow-ups on #3239.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-19 01:57:14 +08:00
Marco Arnulfo d7f8ba8b57 fix(forms): Tab and Space reach check boxes and radio buttons, and a click focuses them (fixes #2223) 2026-10-18 19:55:32 +02:00
mtlcl410andClaude Opus 5.5 36cfc8566e M10: dynamicRender is read only from markup whose closing tags match (#3180)
Review follow-up on #3192.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-19 01:53:12 +08:00
Marco Arnulfo 1a12b0094d Preferences: always use my default zoom and page layout (fixes #1618) 2026-10-18 19:34:04 +02:00
mtlcl410andClaude Opus 5.5 f62bf71a77 M8: tag cleanup keeps inherited pages, fails closed over replaced streams and in-place elements (#3302)
Review follow-ups on #3285: walk /K before the parent and ID trees and look up
an element's inherited /Pg through /P; drop the structure tree when a cut-short
walk could leave MCRs naming a replaced stream; drop it when an element written
in place tags a removed annotation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-19 01:33:42 +08:00
Marco Arnulfo a4cdbb6235 XFA: decode BMP and TIFF pictures (fixes #1644)
Pictures in templates and data that are BMP or TIFF were left out with a
warning. They are now recognised by their signature, sized from their
headers, and decoded through the same bounded reader as GIF (first page
of a TIFF). A BMP needs a valid info-header size after "BM", so text
starting with those two letters is still not taken for one.

A BMP or TIFF chosen for an XFA image field is kept in the data as
image/bmp or image/tiff, since the layout can now show it.
2026-10-18 19:21:49 +02:00
alehel 9b6e74928f Merge remote-tracking branch 'origin/main' into fix/background-hidden-by-page-backdrop
# Conflicts:
#	ROADMAP.md
2026-10-18 19:17:34 +02:00
Mawfyy 932bf9c6bf M4: Ctrl-drag copies pages in the Organize grid (#2997) (#3092)
* M4: Ctrl-drag copies pages in the Organize grid (#2997)

Dragging thumbnails in the Organize page grid with Ctrl held (Option on
macOS) drops copies at the gap instead of moving the pages: a "+" badge
by the pointer, the copies land at the gap selected, and one undo step
takes them out. The insertion point joins the engine edit
(Edit::DuplicatePages { at }) so the copy is one undo step rather than a
duplicate plus a move.

page_duplicate takes a 1-based "at" (the first copy becomes page "at"),
so agents can drive the same thing, and the UI control channel's
ui.drag modifiers now reach the input state -- which gestures that read
it (this copy-drag, and Alt-drag column select) need.

Closes #2997.

* fix(ui): take the grid's copy modifier from the runtime OS (#3092 review)

Option-drag in the Organize grid moved the pages instead of copying them in a Mac
browser: `cfg!(target_os = "macos")` is false in the wasm build, and eframe sets the
context's OS from the browser. `copy_modifier` now takes the context and asks
`commands::mac_shortcuts`, as the shortcut labels do.

The drag helpers in `tests/editing.rs` and `tests/control.rs` picked the modifier the
same compile-time way, so they would have sent Ctrl on a Mac host; both read the
context's OS now. The new regression test
`option_copies_and_ctrl_moves_when_the_context_reports_macos` drives a Mac context —
Ctrl moves, Option copies — and fails without this change.
2026-10-18 11:13:22 -06:00
jonathanwxh-cell bec4e807b7 M2: integrate current main into PR #2960 2026-10-19 01:02:08 +08:00
Marco Arnulfo 9f536c9efd M12: Compare panel filters text changes and visual differences (gap 21) 2026-10-18 18:59:13 +02:00
Marco Arnulfo fcdf2a2146 Layers panel: Reset to Initial Visibility
Layer options in the Layers panel header resets every layer to the state the
file's default configuration (/OCProperties /D) gives it, as Acrobat does.
Visibility is view state, so the reset doesn't mark the document changed and
doesn't enter Undo. doc_info now reports each layer's initial state.
2026-10-18 18:56:40 +02:00
Marco Arnulfo 3468c8543f fix(print): disable Print on documents whose security forbids printing (part of #2669) 2026-10-18 18:56:08 +02:00
PerchAnton 4119359c7d M12: retain accepted whitespace when structured XFA data cannot sync 2026-10-18 19:54:41 +03:00
mtlcl410andClaude Opus 5.5 ce3c0e46c9 M8: redaction clears tag text of elements reached only through the parent or ID tree, and fails closed past the walk limit (#3302)
tags::clean, which drops /Alt, /ActualText and /E that repeat redacted
text, walked the structure tree from the root's /K only, so an element
listed only in /ParentTree or /IDTree kept the redacted words. It now
starts from both trees too, as drop_object_refs does since 3f543ca1.

Past its 2,000,000-reference cap the walk used to skip the rest and
report success. It now fails closed: every structure element in the
document, found by reading each object rather than through the tree,
loses its alternate and actual text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-19 00:52:18 +08:00
mtlcl410andClaude Opus 5.5 e6de108972 M8: note that structure elements written in place keep their references (#2447)
Review note from Losminek on #3285.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv2WwdgjmBz33GWkwgNn3Q
2026-10-19 00:44:58 +08:00
enes yıldızandClaude Opus 5.5 882b5a33bc M1: merge upstream main into claude/fix/2670-cli-owner-password
Merges #2813 (dynamic-XFA layout in the CLI) into the owner-password branch. In
info, text and render the engine Session and the cos document keep the typed
password, while anything handed to `inspect` or `PageRenderer` gets the render
password (the user password recovered from an R2-R4 owner password), including
the dynamic-XFA detection in render and the original-bytes check in text.

Checked: cargo fmt --check; clippy -p pdfcraft-cli -p pdfcraft-engine --all-targets
-D warnings; cargo test -p pdfcraft-cli (75 passed) and -p pdfcraft-engine (283 lib
+ integration passed); cargo xtask layers, assets, parity; a throwaway RC4-40,
RC4-128 and AES-128 dynamic-XFA check with the owner and user passwords through
info, text and render (not committed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 19:44:21 +03:00
PerchAnton 12210a350d M12: retain product notes while integrating current main 2026-10-18 19:42:01 +03:00
PerchAnton b7b4ab0c8d M12: retain product notes while integrating current main 2026-10-18 19:39:17 +03:00
mtlcl410andClaude Opus 5.5 9194ec457f M7: the paragraph editor draws on its page only (#3061)
A paragraph running past its page's bottom edge, as HTML-to-PDF converters
write when they rely on the page clip, opened in an editor that covered the
next page with its box and text, and paragraph outlines crossed the gap
between pages. The editor is clipped to its page within the document area
(it also covered the tool bars when scrolled up), and outlines to their page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-19 00:38:46 +08:00
MarekSpodBiedry 853851075f Mark the alpha changes as PdfCraft patches 2026-10-18 18:37:46 +02:00