Commit Graph
188 Commits
Author SHA1 Message Date
Combiler e85c654ead Merge branch 'main' into main 2026-10-23 16:06:49 +00:00
f46268fbe4 M9: visible signatures read upright on rotated pages (#4067)
* M9: visible signatures read upright on rotated pages

A visible digital signature placed on a page with /Rotate was drawn in
user space, so its graphic and details turned with the page: on scans
stored landscape with /Rotate 270 the signature came out sideways.
Stamps (#1398) and Fill & Sign marks (#2392) were already corrected;
signatures were not.

A new signature widget on a turned page gets /MK /R equal to the page
rotation (inherited rotation included, via pdfcraft_model::pages), and
its appearance is laid out in the box as displayed (sides swapped for a
quarter turn) with a /Matrix from pdfcraft_model::view_matrix_for that
maps it exactly onto /Rect. Signing an existing field follows that
field's own /MK /R, so a field without one is drawn as before. Pages
that are not turned produce the same bytes as before.

Tests: sign crate (layout equals the upright box's, /MK /R, /Matrix,
inherited /Rotate, existing fields with and without /MK /R, malformed
/MK /R) and an engine render test on 0/90/180/270 pages with the
quadrant image. Both fail with the fix reverted.

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

* Keep one evidence/notes pair in the sign.digital parity entry

---------

Co-authored-by: vjeko2404 <vjekoslav@outlook.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 10:43:54 -04:00
Jayesh-Prajapati9 1f8811761a M14: update ctutils to 0.4.5 for RUSTSEC-2026-0385
The dependency-advisory gate rejects ctutils 0.4.2 (non-constant-time
mask generation and u128 comparisons, fixed in 0.4.4). It comes in
through crypto-bigint for the signing crates. Only its version and
checksum change in Cargo.lock.
2026-10-23 18:15:48 +05:30
Combiler01 45fe40e1d7 Dependencies Fix 2026-10-23 16:08:07 +05:30
Combiler01 91c575b1df Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	crates/edit/src/images.rs
#	crates/engine/src/tests.rs
#	crates/xfa/src/script.rs
2026-10-23 11:08:12 +05:30
Brandon ThomasandClaude Opus 5.5 53199fb4fa Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:06:13 -04:00
skylar taylor-barrick adea2c908a M14: recover existing macOS PDF associations after open and save (#3558)
* M14: recover existing macOS PDF associations

* M14: address macOS association recovery review

* M14: reuse upstream save metadata preservation
2026-10-22 15:49:13 -06:00
Brandon ThomasandClaude Opus 5.5 39844f06b5 Release 0.12.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:39:48 -04:00
aiongg b72078871a fix(content): end inline images where the renderer ends them
Filtered inline image data has no length, so its end is an EI chosen
among candidates, and the content parser chose by a different rule
than the renderer's tokenizer (hayro-syntax): the renderer also takes an
EI with no whitespace before it, and checks a candidate by reading what
follows as operators. With image data such as "7FEI 1 0 0 1 200 0 cm",
the renderer ran a cm that redaction read as image data, so text was
drawn 200 pt from where removal and its verify pass looked.

The parser now takes each inline image's end from the renderer's
tokenizer (already in the tree through pdfcraft-cos), so both read the
same operators after the image; a test runs both on the same fixtures.
Only where that tokenizer reads no image (no EI it accepts, or
parameters it can't read) does the parser fall back to its own rules,
counting the image in Parsed::ambiguous_images, and applying redactions
refuses such a page as unreadable.
2026-10-22 15:17:44 -04:00
Combiler01 4d93503937 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	ATTRIBUTION.toml
#	crates/automation/src/tools.rs
#	crates/automation/tests/automation.rs
#	crates/edit/src/images.rs
#	crates/edit/src/lib.rs
#	crates/edit/src/tests.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/create_ui.rs
#	crates/ui-egui/src/edit_text_ui.rs
#	crates/ui-egui/src/editing.rs
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/tests/content.rs
#	crates/ui-egui/tests/editing.rs
2026-10-22 13:32:03 +05:30
Combiler01 67f1a57f08 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	ATTRIBUTION.toml
#	crates/ui-egui/src/content_ui.rs
#	crates/ui-egui/src/i18n/ar.tsv
#	crates/ui-egui/src/i18n/bg.tsv
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/kk.tsv
#	crates/ui-egui/src/i18n/lt.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sl.tsv
#	crates/ui-egui/src/i18n/te.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/panels.rs
2026-10-22 11:59:39 +05:30
aiongg 2c13fd9373 Merge r2 track t16: mixed: automation-mcp, crate:misc, crypt-security, export-convert, optimize, preflight-pdfa, save-io
# Conflicts:
#	ATTRIBUTION.toml
#	contributors/people.toml
#	crates/automation/src/comments.rs
#	crates/edit/src/tests.rs
#	crates/engine/src/tests.rs
#	parity/acrobat-features.toml
2026-10-22 02:23:58 +00:00
aiongg 8bb2eef740 Merge r2 track t13: cos-parse, vendored-crate fixes
# Conflicts:
#	ATTRIBUTION.md
#	crates/annot/src/tests.rs
#	crates/forms/src/tests.rs
#	crates/organize/src/tests.rs
2026-10-22 02:21:53 +00:00
aiongg 2363b0619b Merge r2 track t09: print
# Conflicts:
#	ATTRIBUTION.toml
#	crates/ui-egui/src/lib.rs
2026-10-22 02:21:06 +00:00
aiongg a447dd3db5 Merge PR #2731: Layers panel: Reset to Initial Visibility
Conflicts:
- ATTRIBUTION.toml: ours (sha256 lines only); hashes recomputed at the end of the track
- crates/engine/Cargo.toml: main's workspace chrono entry, plus the PR's pdfcraft-testkit dev-dependency
2026-10-22 01:48:51 +00:00
aiongg a663f2e9c8 Merge PR #2825: fix(cli): info, text, render and audit take the owner password of R2–R4 files 2026-10-22 01:48:29 +00:00
aiongg 84e091d9b3 Merge PR #3195: Vendor and patch hayro-cmap for tolerant CMap parsing
ATTRIBUTION.md: generated header count set to 277 (main's 276 plus the CMap blob moved from bundled to asset).
2026-10-22 01:48:06 +00:00
aiongg 0859c9574d Merge PR #3042: fix(save): preserve destination extended attributes on macOS 2026-10-22 01:46:42 +00:00
aiongg 6a74968144 Merge PR #3633: fix(print): retain existing appearances without Subtype
ROADMAP.md, docs/roadmap.md: kept both sides' log lines.
crates/print/src/tests.rs: kept both sides' additions.
2026-10-22 01:46:31 +00:00
Dylan M. Taylor 606c560d24 Use chrono instead of hand-rolled UTC conversion (#3824)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the hand-rolled civil_from_days/days_from_civil copies and the
leap-year/month math around them (log timestamp, PDF /Info dates, ASN.1
UTCTime/GeneralizedTime, FormCalc Date/Num2Date, the Combine modified-time
stamp, the form date picker, and the demo-PDF timestamps) with chrono as a
workspace dependency. The engine's existing chrono (clock) is lifted to the
workspace. Every output format is unchanged and pinned by tests. CLAUDE.md now
says time goes through chrono.

Follow-up to storytold/cadcraft#1785

* Guard chrono date paths: no panic on overflow, no '+' on 5-digit years

Systemic chrono follow-ups to the UTC/calendar PR:

- logging::timestamp and combine_ui::utc_stamp dropped chrono's
  From<SystemTime> (which unwraps internally and panics on a clock past
  year 262143) for the fallible DateTime::from_timestamp, reading an
  out-of-range clock as the epoch, as the old clamping code did.
- timestamp, utc_stamp, pdf_date and the xtask Stamp now write the year
  with {:04} instead of %Y, which prefixes a '+' before years past 9999;
  the old hand-rolled code wrote the plain zero-padded year.
- forms_ui calendar navigation and length, and formcalc days_in, use
  checked_add for the December year successor so y + 1 can't overflow at
  i32::MAX / i64::MAX (reachable from a parsed or persisted date).

Docstrings added to the chrono-touched civil_from_days and Time::from_unix
for the docstring-coverage gate. Tests cover the out-of-range-epoch and
10000-01-01 cases for each.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* Keep FormCalc dates exact past chrono's range

MAX_DAYS accepts day numbers up to 1e8 (years past 270000), but NaiveDate
stops near ±262143, so Num2Date(99000000) and Format fell back to
1900-01-01. days_from_civil, civil_from_days and days_in now have chrono
convert within the date's 400-year Gregorian cycle and add whole cycles
back (via i128), which gives the pre-chrono results for every year. Tests
cover accepted inputs past chrono's range on both sides of the epoch,
through Num2Date and Format, with values from the old formula.

* Bump rustls-webpki and webpki-root-certs for RUSTSEC-2026-0382/0383

rustls-webpki 0.103.15 -> 0.103.16 and webpki-root-certs 1.0.9 -> 1.0.10
(both semver-compatible, lockfile only) drop the distrusted emSign
intermediates and root. Only these two entries change; cargo deny check
advisories passes. Upstream main fails the same check.

* logging/cos/sign/combine/demo: keep extended-year dates, no epoch fallback

For consistency with the stricter no-epoch-fallback rule now agreed across the sibling
chrono PRs (wordcraft#3427, filmcraft#2662): `DateTime::from_timestamp(..).unwrap_or_default()`
changes a timestamp the old code converted past chrono's year 262143 range — it collapses
to 1970-01-01 instead of the real proleptic-Gregorian date. The FormCalc Num2Date path was
already fixed (FredApps approved); this applies the same fix to every remaining conversion.
Each site uses the 400-year Gregorian cycle (146_097 days) with chrono as the only calendar
logic: chrono converts the day within one cycle, whole cycles carry in the year (i128,
saturated into i64). PDF/ASN.1 format-specific year handling is kept exactly as the old code
had it. Verified the cycle result equals the old formula across a wide range and at the extremes.

- apps/pdfcraft/src/logging.rs `timestamp`: cycle `civil_from_days(u64)`; pre-1970 epoch clamp
  and ms/time-of-day format kept. Pins: +10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
  (was 1970); checked_add u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch).
- crates/cos/src/writer.rs `pdf_date` (i64): cycle converter, no year clamp (old code wrote `y`
  as-is, so a negative year keeps its sign). Pins: pdf_date(10_000_000_000_000)="D:3188570520174640Z",
  pdf_date(-10_000_000_000_000)="D:-3149180813061320Z".
- crates/sign/src/der.rs `Time::from_unix` (i64): cycle converter; the `year` field is a u32, so
  the year is cast `as u32` (wrapping) exactly as the old code did — ASN.1 year width preserved
  byte-for-byte. Pins: from_unix(10e12).year=318857, from_unix(-10e12).year=4_294_652_378,
  from_unix(i64::MAX).year=219_250_468, from_unix(i64::MIN).year=4_075_720_767.
- crates/ui-egui/src/combine_ui.rs `utc_stamp` (u64): cycle converter; pre-1970 epoch clamp kept.
  Pins: utc_stamp(10e12)="318857-05-20 17:46 UTC", u64::MAX/2 s="292277026596-12-04 15:30 UTC".
- xtask/src/demo_pdf/pdf.rs `Stamp::from_unix` (i64): cycle converter, no year clamp. Pins:
  from_unix(10e12).iso="318857-05-20T17:46:40Z"/.pdf="D:3188570520174640Z",
  from_unix(-10e12).iso="-314918-08-13T06:13:20Z".

cos/sign/xtask built, tested (pdf_dates_are_utc_and_byte_identical, der::tests::times,
stamps_round_trip), fmt and clippy clean. pdfcraft-ui-egui and the pdfcraft app can't link
here (unrelated LLVM intrinsic bug in the rten-gemm dependency, 1.98.1), so their logging and
combine_ui functions + tests were copied verbatim into a scratch crate with chrono 0.4.45 and
pass there; fmt --check clean on all five packages. CI builds these on all platforms.

* Don't build a year-318857 SystemTime unchecked in the logging test

Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-21 19:36:46 -04:00
6898094e77 M6: renaming a field renames it in the calculations that read it (#3550)
* M6: renaming a field renames it in the calculations that read it

A rename silently broke every formula that used the field: a sum dropped
it, notation read it as 0 and a script kept a stale value. set_props now
rewrites the name in each field's Calculate script, in the same edit:
AFSimple_Calculate field lists, simplified field notation (the BVCALC
comment and its translation) and getField calls naming it in one string
literal. The rest of each script is kept as written; names built at run
time, non-UTF-8 script streams and a name another field still has are
left alone, and a self-check keeps a script whose meaning would change.

Notation is read by one lexer shared by notation_js, eval_notation and
the rename. form_set_props returns formulas_updated; Field Properties
says how many calculations now use the new name.

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

* M6: form_set_props leaves the renamed field out of formulas_updated by object

The renamed field was matched by the name given, but set_props stores it trimmed and under
its parent's prefix. When the same call changed the field's own formula, formulas_updated
listed the field as one the rename had rewritten.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-10-21 19:25:43 -04:00
Brandon ThomasandClaude Opus 5.5 3e3064532f Release: PdfCraft v0.11.0 (#3893)
* Release: PdfCraft v0.11.0

- Bump the workspace to 0.11.0.
- Fix main's red CI (cargo-deny): update rustls-webpki 0.103.15 -> 0.103.16
  (RUSTSEC-2026-0382) and webpki-root-certs 1.0.9 -> 1.0.10 (RUSTSEC-2026-0383),
  both reached through winprint's sigstore build dependency.
- `cargo xtask version set` now moves the workspace crates' pins in
  [workspace.dependencies] with the version; before, `cargo update --workspace`
  refused the bump and the pins had to be edited by hand.

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

* Tests: a healthy private retry may answer during the watchdog pass that scheduled it

shared_timeouts_retry_healthy_pages_privately_before_blacklisting failed on
CI (PR #3893): the try_recv that runs the watchdog spawns the private retries,
and page 1's retry has no gate, so on a loaded runner its healthy result
arrived before that same call read the channel. Accept that early answer
(page 1, no error) and use it as the healthy result; an error still fails.
Reproduced by sleeping 300 ms after the watchdog in try_recv: the old test
fails every time, the new one passes.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 15:27:33 -04:00
Combiler01 72028ba3cf fix: update rustls-webpki to patch RUSTSEC-2026-0382 2026-10-21 18:54:08 +05:30
Combiler01 96d738677b Update webpki-root-certs to 1.0.10 (RUSTSEC-2026-0383) 2026-10-21 18:44:57 +05:30
Combiler01 b3b555ff5e Fixes the bug regarding duplicate images 2026-10-21 16:33:31 +05:30
Combiler 4d12633cb1 Merge branch 'storytold:main' into main 2026-10-20 16:21:11 +00:00
Brandon ThomasandClaude Opus 5.5 b77d57000c Release 0.10.1: rebuild with the shared workflow's exact Linux tarball pick
0.10.0's Arch Linux and Flatpak aarch64 jobs picked up the
pdfcraft-cli tarball (storytold/craft-common-actions#35).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:38:32 -04:00
Combiler beb1a70d71 Merge branch 'main' into main 2026-10-20 15:26:13 +00:00
Ahmad Mujahid 1489436392 fix(print): preserve existing appearances without Subtype (#3532) 2026-10-20 14:34:40 +04:00
Brandon ThomasandClaude Opus 5.5 6fb6b7cf6f Release 0.10.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:21:10 -04:00
PerchAnton a33cf0fe36 M1: resolve main integration for PR #3042 2026-10-19 18:43:18 +03:00
7d9adcb8f1 fix(preflight): identify PDF standards by XML namespace (#3364)
* fix(preflight): identify standards by XML namespace

* fix(preflight): bound XMP attribute parsing work

* fix(preflight): cap XMP during stream decoding

* Preflight: read the PDF/A and PDF/UA identification by its usual prefix when the XMP can't be parsed as XML

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-19 02:48:40 -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
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
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
Charan Rathore bf27cb259a Vendor and patch hayro-cmap for tolerant CMap parsing
A /ToUnicode CMap with any unparsable part made the reading path
discard the whole map and fall back to the font encoding, so search
and redaction missed text the edit layer could read.  Vendor
hayro-cmap 0.1.0 and patch it to skip malformed bfchar/bfrange
entries, unresolvable usecmap references and unparseable tails,
keeping every readable entry, matching MuPDF/pdftotext and
PdfCraft's own edit-layer parser.

Fixes #3177.

Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>
2026-10-18 13:07:15 +05:30
PerchAnton 095e1d36bd chore(pr-3042): integrate main d8b27058 2026-10-18 08:36:10 +03:00
skylar taylor-barrick d36a2ccbf7 Image Arrange: keep the image's clipping and position through later edits
- Remove Crop rebuilds the image's placement from its drawn CTM instead of
  pattern-matching cm ops. After an Arrange under a translated CTM it used to
  keep the absolute placement and draw the image displaced.
- Arrange refuses a move that would change which clips apply to the image
  (moving it past a top-level clip it isn't under, or out from under one
  without redrawing it), instead of silently cropping it.
- The exact-clip test now checks the moved copy carries its own clip and
  hit-tests both images.
- Cargo.lock: add pdfcraft-edit's pdfcraft-render dev-dependency, which the
  previous lockfile commit left out (CI failed --locked).
2026-10-17 22:04:17 -06:00
skylar taylor-barrick 6eaec60aba Keep main's Cargo.lock; add only pdfcraft-edit's new pdfcraft-render dev-dependency 2026-10-17 21:30:47 -06:00
skylar taylor-barrick 4938bbf272 Merge storytold/pdfcraft main (picks up the zbus advisory ignores) 2026-10-17 20:44:19 -06:00
Brandon ThomasandClaude Opus 5.5 645016addc Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:43:00 -04:00
skylar taylor-barrick d9141f9336 Merge storytold/pdfcraft main; refresh ATTRIBUTION.toml translation hashes 2026-10-17 12:44:10 -06:00
skylar taylor-barrick 5cf11e9b8d Build: take craft-docpath from the merged craft-libs rev
craft-libs#169 is merged, so the fork pin is no longer needed. Pinned to
storytold/craft-libs 82a24b006eefa036b43c09a67fb9a3fbd17ffe63 (main after the merge) rather than the older 12281d07,
which predates resolve_absolute and the Windows colon refusal.
2026-10-17 12:03:16 -06:00
Combiler01 1d2af87b84 test: implement deterministic mock clipboard to resolve Windows heap corruption 2026-10-17 21:36:48 +05:30
PerchAnton bac52a6d9e fix(save): preserve macOS extended attributes (#2658) 2026-10-17 15:53:15 +03:00
aiongg 77776ca2c6 Build: use craft-docpath from craft-libs 2026-10-17 02:21:34 +00:00
enes yıldızandClaude Opus 5.5 06c4a970a2 fix(cli): info, text, render and audit take the owner password of R2–R4 files
`pdfcraft-cli info`, `text`, `render` and `audit` rejected the owner
password of RC4-40, RC4-128 and AES-128 files with "the password is
incorrect", while `run doc_open` accepted it (#2670, item 8). The renderer
authenticates only the user password of revisions 2–4. `Session::open`
works around that by handing it the user password that owner
authentication recovers, but the CLI passed the typed password straight to
`inspect` and `PageRenderer`.

`pdfcraft_engine::render_password_for` now gives any render path outside a
session the password the renderer takes, sharing the recovery with
`Session::open`. The CLI uses it for the renderer and keeps the typed
password for the engine session and the cos document, so the owner
password still lifts copy restrictions in `text`.

The render test decodes the PNG and checks its dimensions and that it
has non-white pixels, for every algorithm with either password.

Refs #2670 (item 8 only).

Checks: cargo fmt --check; cargo clippy -p pdfcraft-cli -p pdfcraft-engine
--all-targets -- -D warnings; cargo test -p pdfcraft-cli; cargo test -p
pdfcraft-engine (and --lib); cargo xtask layers, assets, parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:15:22 +03:00
aiongg 265403813b fix(links): resolve remote document paths with the shared path check
GoToR file specifications go through craft-docpath: network, device and
object-manager paths (\??\, /??/) are refused without touching the
filesystem, relative targets resolve against the source document's
folder, and a result that is not absolute is refused rather than read
against the working directory. Files named on the command line open
under their absolute path, so their relative links keep resolving.
2026-10-16 18:16:50 +00:00
Combiler01 b31e6b49db merge: reconcile upstream/main with local changes 2026-10-15 19:36:23 +05:30