Commit Graph
1451 Commits
Author SHA1 Message Date
Mex 14c3d951e7 M6: preserve complete plain XML form values 2026-10-15 23:04:14 +02:00
Tanish MankuandClaude Opus 5 99941daa5a docs(roadmap): record the day's two changes
`CLAUDE.md` asks for a progress-log line and a status line at the end of a session.

- The Annex D.2 alias codes, in both tables (`13e3154a`).
- Reading order in the text oracle (`50cbf5fa`), with gap 12's revision history.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 13:34:38 -07:00
Muhammad Saad c9956bf77f Replace Text: give the caret its own /NM (#2666)
add_text_replacement built the StrikeOut and the Caret from one Meta, and
add_annotation stamps /NM from meta.id, so the pair shared a single name. XFDF
names a reply by the /NM of what it replies to, so the exporter wrote the
caret's inreplyto as the caret's own name. Importing that file found a comment
replying to itself, and the Replace Text comment did not arrive: the reporter's
second document listed none. FDF was unaffected because it keeps the pair by
object structure rather than by name.

Take a second Meta for the caret. The ids stay supplied by the caller, as the
rest of the crate does so edits remain deterministic, and the one production
caller already has a generator and calls it twice. /IRT still points at the
strikeout, so the group still moves, lists and deletes as one.

Tests: the annotation test now asserts the two carry different names and that
the caret's /IRT resolves to the strikeout's. A new XFDF test exports a Replace
Text pair and imports it into another document, asserting both comments arrive
with their thread intact; it fails if the two are given one id again.
2026-10-16 00:34:21 +04:00
Tanish MankuandClaude Opus 5 50cbf5fa19 feat(oracle): score reading order, which word F1 cannot see
`cargo xtask text-oracle` reported one number: word F1 against `pdftotext`. F1 is a bag of
words. It catches a character read wrong, a glyph not read at all, a font we cannot map -- and
it is blind to order, so a page emitted back to front scores a perfect 1.000. Reading order is
most of what text extraction has left to get wrong: a column read into the middle of its
neighbour (#2387), a figure's labels threaded through a paragraph (#2586), a form's value parted
from its label.

The hole is not hypothetical. Reviewing #2431, a change that moves text and alters nothing else,
**word F1 called 26 of the 29 files whose output changed identical**, while scoring the word
sequence separated 18 improvements from 6 regressions and named the shape of the regressions
(forms: `prefilled_f1040.pdf` 0.672 -> 0.481, a checkbox's number parted from its label). The
harness could not see the only thing that change does.

So a second score per file: the longest common subsequence of the two word sequences, as a share
of the longer one. It shows up in the first three corpus files -- `Test-plusminus.pdf` scores F1
1.000 and reading order 0.875, the same forty words in a title block that we read down the
column and `pdftotext` reads across.

Both sides are cut to the first 3,000 words, because the comparison is quadratic and three pages
is plenty of signal; the run says how many files that bit, so the cap cannot quietly become the
measurement. It reports; it does not gate, and like the pixel score it is a *disagreement* with
Poppler rather than a verdict -- in the #2431 measurement the same split scored as a win on one
file and a loss on another purely because `pdftotext` makes it in one place and not the other.
The function's doc comment, `crates/oracle/README.md` and `docs/gaps.md` gap 12 all say so.

`words` now takes the sequence rather than re-splitting the text, so both scores read the same
tokens. `docs/file-format-parity.md` claimed plain-text export was good "(word-F1 0.98)" in a row
whose own column is reading order; it now says the two are scored separately.

Verified: `cargo xtask text-oracle --limit 60` end to end; `cargo test -p xtask` 49 passed; fmt
and clippy clean. The unit test was watched failing with the score blinded to order (sorting both
sides made it 1.000 where it must be below 0.7). The corpus-wide first reading is not in this
commit: the machine is shared with a parallel build and the full sweep was stopped twice for
memory.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 13:33:22 -07:00
Charan Rathore 53425fc606 test(annot): fix final-line indentation
Corrects the closing-brace indentation on the last line so cargo fmt --check stays clean.

Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>
2026-10-16 01:57:38 +05:30
Charan Rathore 8947465af7 test(annot): cover 7-bit ASCII percent-encoding of link /URI
Regression test for the /URI percent-encoding fix: round-trips a non-ASCII URL and an already-escaped URL through save/reload and asserts the stored URIs are pure ASCII with existing escapes preserved.

Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>
2026-10-16 01:56:26 +05:30
Charan Rathore 46ebc6dcf7 fix(annot): percent-encode non-ASCII bytes written into link /URI
A link annotation whose target contains non-ASCII characters (for example https://example.com/ação) was written into the PDF /URI string as raw UTF-8 bytes, but URIs in PDF are 7-bit ASCII strings (ISO 32000 §12.6.4.8). The saved file then carried an out-of-spec URI that strict readers can reject or misparse.

Percent-encode every byte outside the printable ASCII range when writing the /URI entry. Bytes already inside 0x21..=0x7E pass through unchanged, so existing percent-escapes like %20 are preserved.

Adds a regression test that round-trips a non-ASCII URL and an already-escaped URL through save/reload and asserts the stored URIs are pure ASCII.

Signed-off-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>
2026-10-16 01:55:31 +05:30
NetterSilverandClaude Haiku 5.5 bafb948226 Round FormCalc Str and Format exact ties away from zero
Found while testing crates/js/src/formcalc.rs:2169 (Str) and :2258 (Format number picture)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 22:19:58 +02:00
Mex f1177bb287 M6: refresh XML whitespace fix and record parity evidence 2026-10-15 22:01:42 +02:00
Tanish MankuandClaude Opus 5 13e3154ab3 fix(fonts): read the Annex D.2 alias codes as the ink they draw
#2305 fixed WinAnsi 0xAD in `crates/fonts`, and closed with two loose ends it named: the
user-visible extraction path goes through hayro, not that table, so it still reported the
invisible character; and the same Annex D.2 note makes 0xA0 the `space` glyph. This finishes
both, and makes the two tables in the product agree about the same byte.

WinAnsiEncoding gives codes 0xA0 and 0xAD the glyph names `space` and `hyphen` -- the names it
also gives 0x20 and 0x2D -- and MacRomanEncoding gives 0xCA `space`. The tables called them
`nbspace` and `sfthyphen`, whose Adobe Glyph List values U+00A0 and U+00AD are invisible
formatting characters, so extraction hid ink the reader can see: "well-known" drawn with the
0xAD byte matched no search and no redaction pattern, and a non-breaking space did not match the
space a reader typed.

Evidence. Two sources transcribe the table the same way (an Annex D implementation's docs, and
the WinAnsi.enc encoding vector R ships, which gives `/space` at octal 240 and `/hyphen` at
255), and a black-box check settles the rest: on a synthetic fixture pdftotext reads the
base-encoded codes as `a-b a b`, and keeps U+00AD for a code an explicit `/Differences` names
`sfthyphen`. We now do both. Over the 989-file pdf.js corpus the soft hyphens we extract fall
from 117 to 13 and the non-breaking spaces from 30 to 26; everything left comes from `/ToUnicode`
maps, which say what they mean and are not touched.

Scope. The base table only, in the text direction only.

- A `/Differences` entry naming `sfthyphen` or `nbspace` is a deliberate statement about one
  code and keeps its glyph list value, as pdftotext does.
- Glyph lookup is untouched, so a font program's own `sfthyphen` program is still found first.
  In hayro that is why the alias sits in the Unicode path rather than in the generated encoding
  table: `code_to_ps_name` is shared with lookup.
- Writing is unaffected: `encode` takes the lowest code showing a character, so ' ' and '-'
  still write 0x20 and 0x2D, and a caller holding a literal U+00A0 still writes 0xA0.

Tests. `fonts` pins both tables and the whole decode/encode path including the `/Differences`
case; `render` pins the extraction a reader actually gets, over WinAnsi, MacRoman and
`/Differences`. Each was watched failing with each cell and each match arm reverted in turn, so
no assertion is passing by accident. fonts 70, render 296+4, redact 49, a11y 43 and edit 156 are
green, with fmt, clippy, `xtask layers` (37 crates) and `xtask assets` clean.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 12:52:53 -07:00
Mex 87e69d2c72 M4: merge current upstream into indirect rotation fix 2026-10-15 21:48:28 +02:00
Mex 7bb2d49467 M1: merge current upstream into Flate fallback fix 2026-10-15 21:40:21 +02:00
outragedevs 4a991b03b5 M7: Let the page range select page 1 alone
Seitenbereich „1 bis 1“ springt nicht mehr auf die letzte Seite zurück.
2026-10-15 20:21:57 +02:00
Mateus BessaandClaude Opus 5.5 64ab6b67ec fix(ui): Copy in the selection action bar closes the bar
The bar beside selected text closed after Highlight, Underline, Strikethrough,
Add comment and Replace text because those consume the selection. Copy kept the
selection, so the bar stayed open. Remember the copied selection and hide the
bar until the selection changes; the words stay selected for ⌘C or another
action.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 15:14:30 -03:00
mtlcl410andClaude Opus 5.5 294fea2fb8 fix(ui): Reveal in File Explorer selects files whose path has a space (#2579)
Explorer reads /select,"<path>". The path went in as one ordinary
argument, which Rust quotes whole when it contains a space
("/select,C:\a b.pdf"), and Explorer answers that by opening Documents.
A file saved to a folder or name with a space (often after Save As) was
therefore never shown. reveal_command now quotes the path after the
comma and the Windows runner passes the arguments as written; a path
containing a quote, which Windows doesn't allow, is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-16 01:34:32 +08:00
magdale76andClaude Opus 5.5 67d35b70a0 M14: no PdfCraft lion over PDF thumbnails and the preview pane
With PdfCraft as the default PDF program, Windows painted the program's
icon, the lion, over the lower-right corner of every PDF thumbnail and of
the preview pane, covering part of the page. Thumbnail overlays come from
the ProgID's TypeOverlay value. When it is absent, Windows uses the default
program's icon, and an empty string means no overlay ("Thumbnail Handlers",
Microsoft Learn). The installer now writes TypeOverlay = "" on
PdfCraft.Document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:09:16 +02:00
NetterSilverandClaude Haiku 5.5 db4157099a Don't treat a date ending in "Sep" as PM in AFDate/AFTime parsing
Found while testing crates/forms/src/af.rs:590

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 15:43:14 +00:00
NetterSilverandClaude Haiku 5.5 7cc073ab89 Shorten over-long part file names in doc_split by bookmarks
Found while testing crates/automation/src/lib.rs:1747

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 17:42:18 +02:00
oracola91 f59320b1a0 fix(web): host mode needs a parent page on the same origin
With ?host=parent, saves are posted to the parent with the app's own
origin as the target. A framing page on another origin never gets them:
the browser drops each message without an error, post() returns Ok, and
the document is marked saved and the leave-page warning cleared (#2167).

Bridge::new now returns None unless the parent's origin is the app's
own (reading another origin's location throws, which is None too). The
app then runs as if unframed and saves download, and a console warning
says why. Same-origin embedding is unchanged.
2026-10-15 18:36:56 +03:00
Verix bdb943dc3d M3: a page jumped to in single-page view no longer hops into place
The jump scrolls to MARGIN - GAP above the page. A page that fits has
nothing to scroll, and egui clamps the offset only after drawing the
frame, so the page showed 14 pt too high for a frame and then hopped
down. Clamp the jump to the scroll range before handing it to egui.
2026-10-15 17:28:46 +02:00
jonathanwxh-cell 279712769d fix(edit): keep overlapping watermarks and distinct paragraphs on text edit (#1756) 2026-10-15 23:22:11 +08:00
komo1iddinandClaude Opus 5.5 8ebe4f7a76 M7: write a full font program compressed and find it again on any page
A text edit that needs glyphs the source font's embedding lacks writes
the face's full program once. It was written uncompressed, and the
document-wide search that lets another page reuse it compared the
stream's raw bytes, so it only matched an uncompressed copy.

Write the program with Flate, and compare its decoded bytes (after the
/Length1 check) both on the page and across the document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 20:21:02 +05:00
Verix ff526a7896 M3: single-page view turns pages without a blank frame
At Fit Page in single-page view, every wheel turn forward drew one
blank frame before the new page appeared.

- Lay out the shown row again after the wheel is read. The jump to the
  new page was measured from the old row's place and scrolled both out
  of view; going back it came out negative and was clamped, so only
  forward turns flickered. The edge-turn deferral that worked around
  this goes.
- Prefetch the rows either side of the shown one, so the next page is
  rendered before the turn instead of showing blank paper.
2026-10-15 17:19:56 +02:00
oracola91 00fb10cf1e fix(crypt): /Perms limits what an edited /P allows
AES-256 files (R5 and R6) store the permissions twice: in clear in /P,
and in /Perms, encrypted with the file key (ISO 32000-2, Algorithm 13).
We wrote /Perms but never read it, so a document opened with the user
password got whatever /P said, even when /P no longer matched /Perms.

permissions() now decrypts /Perms and, when it holds a valid block
(marked "adb"), counts a permission only if both /P and /Perms grant
it. Without a readable /Perms, /P applies as before. R2 to R4 need no
check: /P is part of their file key, so an edited /P does not open.
2026-10-15 17:56:45 +03:00
komo1iddinandClaude Opus 5.5 398ad77077 M7: rewrap a paragraph at a slant at the width its rows have
A turned paragraph's width came from its rows' upright bounding boxes,
turned into the baseline's frame. On a slant that box is wider than the
row by the row's height times |sin 2θ| (about 12 pt for 12 pt text at
30°), so a rewrite let a word that doesn't fit stay on its row.

Each run now also records its box along and across its baseline, from
the same corners as its upright box, and a line unions those. Grouping
and the rewrite's width read that box. Upright and quarter-turned text
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 19:51:57 +05:00
FredAppsandCodex dc41044ba7 Fill Vietnamese and Lithuanian catalog coverage gaps (#2510)
Co-authored-by: Codex <codex@openai.com>
2026-10-15 16:50:50 +02:00
FredAppsandCodex 609a724235 Fix main UI regressions in paging, fonts and test expectations (#2502)
* Fix main UI regressions in paging, fonts and test expectations

* Wait for asynchronous dropped-document preparation in regression

---------

Co-authored-by: Codex <codex@openai.com>
2026-10-15 16:50:45 +02:00
FredAppsandCodex 5259491cf4 Tests: update save and search regressions for current behavior (#2484)
Co-authored-by: Codex <codex@openai.com>
2026-10-15 16:50:39 +02:00
mtlcl410andClaude Opus 5.5 491ce2da5b fix(i18n): zh-hant had two keys twice after concurrent merges (#2414)
the Revert prompt to zh-hant.tsv; merged together, the catalog held both
copies and `bundled_catalogs_are_consistent` (and the tests that load
zh-hant as a reference) failed on main. Keep #1750's wording (字型大小,
「」 quotes, as the rest of the catalog) and drop the other copy.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:50:33 +02:00
mtlcl410andClaude Opus 5.5 1bc3a31814 fix(parity): merge the keys concurrent merges left twice in acrobat-features.toml (#2419)
Nine features carried `evidence`, `notes` (and once `tools`) twice, so the
file was no longer valid TOML and `cargo xtask parity` stopped with
"duplicate key". Each pair is merged into one: evidence and tools as the
union of both lists, notes keeping every fact either side added.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:50:26 +02:00
wstockandClaude Fable 5.1 224416a3dd Tests: the signing test's password meets the 8-character minimum (#2417) (#2420)
signing_with_translated_labels_through_tools (from #1951) creates and uses
a digital ID with the password "secret1"; #2201 ("harden .p12 digital IDs")
now requires at least 8 characters. Each passed alone; together the test
fails on main, and so does every PR's test job. The password is "secret12"
in both places.


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

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-15 16:50:04 +02:00
oracola91 d0a3427184 fix(platform): a save keeps the permissions of the file it replaces
On Unix the staging file got the default mode for a new file (usually
0644) and was renamed over the original, so saving a private 0600 PDF
left it readable by other local users. The staging file is now created
owner-only and given the original's permissions before anything is
written. A new file still gets the usual mode. Desktop save, the CLI
and the automation tools all write through this function.

Fixes #1790.
2026-10-15 16:40:45 +03:00
afa8e1843c M3: Wayland: stop retaining Ctrl zoom for pixel-only wheel input (#1569)
* M3: release Ctrl zoom for pixel-only Wayland wheel events

Use the reported scroll source to avoid unterminated finger gestures for wheel, wheel-tilt and continuous input. Exercise the production policy through egui, including modifier release and preserved finger momentum.

Assisted-by: OpenAI Codex

* M3: keep Cargo lockfile dependency ordering canonical

Record the dependency ordering produced by the completed workspace and wasm gates.

Assisted-by: OpenAI Codex

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:08:34 -04:00
cdf44d7999 Comments panel: page groups fold from their chevron (#1614)
* Comments panel: page groups fold from their chevron (fixes #1554)

* Comments panel: translate the fold-all buttons in every catalog

"Collapse all pages" and "Expand all pages" were missing from the 21
catalogs, so the covers_ui_literals tests failed; the catalogs' hashes
are re-pinned in ATTRIBUTION.toml.

* Add the Vietnamese entries for Collapse/Expand all pages; refresh catalog hashes

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

* Refresh catalog hashes after merging main

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:03:49 -04:00
ab45a1855f Keep annotations readable under reading colours (#1548) (#1557)
* fix(render): keep annotations readable under reading colours (#1548)

Reading colours remapped annotation paints as line art: the yellow
highlight fill is luminance-light, so it was mapped toward the dark
background, and Multiply then darkened the remapped-white words under
it into invisibility.

Two changes in hayro:

- The /Annots loop in interpret_page is bracketed by a new
  begin_annotations/end_annotations Device signal (patch 26), and the
  renderer's reading-colours mapping leaves annotation paints alone.
  Reading colours replace document colours, never the colours a
  comment was given — the Acrobat model.
- The page background is painted inside the page transparency group
  (patch 27), as the initial backdrop, so a Multiply highlight blends
  with the background too. With the background outside the group the
  band kept its own flat colour and the words vanished into it. White
  pages are unaffected: multiplying with white is the identity.

Regression test reading_colors_keep_highlight_annotations_readable
covers both annotation paths (/AP and the no-/AP /QuadPoints
fallback) under WHITE_ON_BLACK; parity entry a11y.replace-page-colors
updated with the behaviour and evidence.

Co-Authored-By: GLM <noreply@z.ai>

* contributors: credit @edurbs as Eduardo Soares

The contributor's own entry, chosen by them (real and display name).

Co-Authored-By: GLM <noreply@z.ai>

* #1557: record the vendored hayro patches in vendor/README.md

Main already uses hayro-interpret patches (26)-(28), so the annotation hooks become
hayro-interpret (29) and the renderer's parts hayro (17) and (18).

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

---------

Co-authored-by: GLM <noreply@z.ai>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:03:25 -04:00
e62d3db31b M6: keep AcroForm XML field tags within the NCName grammar (#1588)
Constrain the existing normalization to XML1.0 name characters while preserving
valid tags and original PDF names through xfdf:original. Verify Unicode names,
prefixes and normalization collisions with saved-PDF round trips.

Co-authored-by: MMexicann <MMexicann@users.noreply.github.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:03:00 -04:00
53b9bc1059 M6: preserve carriage returns in form CSV exports (#1563)
* M6: preserve carriage returns in exported form CSV

Quote CR in names and values so reimport cannot discard a trailing CR as a
record terminator. Verify eleven combinations through saved-PDF round trips.

Co-Authored-By: Codex <noreply@openai.com>

* Keep main's ROADMAP status line

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

---------

Co-authored-by: MMexicann <MMexicann@users.noreply.github.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:02:35 -04:00
ac69c29823 M14: Fix(nixos): discover installed UI fallback fonts on NixOS (#1553)
* M14: discover installed UI fallback fonts through Fontconfig

Find existing fallback faces in configured directories, including Nix store paths. Bound and cache discovery while preserving candidate names, glyph checks and the screenshot opt-out.

Assisted-by: OpenAI Codex

* M14: keep the drop-dispatch regression deterministic

Select inline document preparation in the existing drop-dispatch test, matching the validation fix used by both Wayland branches. Full workspace, strict Clippy and wasm gates pass.

Assisted-by: OpenAI Codex

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:02:11 -04:00
cccd55fab2 M1: edit finds the pages a damaged file's readers find (#1664)
* M1: edit finds the pages a damaged file's readers find (fixes #1656)

* M1: rebuild a lost page tree only when the reader shows the same pages, and refuse edits to signed rebuilt files (#1656)

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 04:01:45 -04:00
9479561ffa Complete the Arabic (العربية) catalog: every missing row translated (#1559)
* Complete the Arabic (العربية) catalog: every missing row translated

ar.tsv covered 2,325 of the interface rows; the newer catalogs (lt, cs,
es and friends) had since grown to a 2,578-row union. This adds the 253
rows Arabic was missing, in the same clean-room Modern Standard Arabic
and Acrobat-style vocabulary the file already uses:

- digital-signature verification detail: every ECDSA/RSA/BER/DER reading
  note, timestamp-token and revocation sentence, the certificate
  inspector's sixteen fields (المُصدر، الرقم التسلسلي، صالح من/إلى…)
  and the key-usage purposes (عدم الجحود، تشفير المفاتيح…)
- the accessibility checker's full vocabulary (وسوم، بنية المستند،
  النص البديل، خلايا الرأس…) and its report grades
- the eight newer @plural rows with all six Arabic forms (صور محسّنة،
  بكسلات، نتائج…) and the calendar's weekday abbreviations
- PDF/A output intents, combine/organize status lines, panel restore
  errors, and the small chrome (اختيار…، تنظيم، تقرير مفصل…).

Same catalog, same lookups — Arabic now covers the union of every
language's rows (2,578).

* Refresh the ar.tsv sha256 in ATTRIBUTION.toml

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

* Refresh catalog hashes after merging main

---------

Co-authored-by: hasan-sdaif <306196098+hasan-sdaif@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:57:18 -04:00
fcd72747cd M6: make list boxes directly selectable and scroll to selection (#1524) (#1543)
* M6: make list boxes directly selectable and scroll to selection (#1524)

- In-place selection: List boxes are rendered directly on the page and
  selectable in place. Clicking an option row selects it immediately;
  Ctrl/Cmd-clicking toggles items in multi-select lists, and Shift-clicking
  selects ranges, without opening a popup menu.
- Appearance and scroll (/TI): When options are selected, /TI (top index)
  is automatically adjusted so the selected items are scrolled into view
  within the widget's visible rows, and option rows and selection highlights
  are drawn into the appearance stream.
- Unit and UI tests: Added tests in pdfcraft-forms verifying /TI scrolling
  and appearance generation for multi-select lists, and UI tests in
  pdfcraft-ui-egui covering in-place click and Ctrl-click toggling.
  Also ensured test harness in reload.rs explicitly sets language to en.

Fixes #1524

* List boxes: keep the engine's page appearance, consume arrow keys

Drop the overpaint of every fillable list box with an egui mock (it ignored the
PDF's fonts, colours and /MK /R; the engine redraws the appearance with the new
/TI), and consume ArrowUp/ArrowDown in the list overlay so they don't also
scroll the page.

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-15 03:52:55 -04:00
d1bc200115 M7: grow text boxes and edit existing text (#1539)
* M7: grow text boxes and edit existing text

Text boxes now grow to the page edge and vertically as needed, retaining their size when saved. Clicking native or added text in either text tool edits the existing item instead of layering a duplicate over it.\n\nCo-authored-by: OpenAI gpt-6.1-sol <noreply@openai.com>

* #1539: build against main's Document::added() accessor and draft-aware prepare

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-15 03:52:33 -04:00
3805be7669 M14: store XDG preferences in configuration directory (#1395) (#1536)
Generated with Codex for @DWAA1660.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:52:12 -04:00
57c8de09ab M6: preserve XFDF field text around XML comments (#1647)
Join the text nodes in each field value so XML comments and processing instructions do not truncate imported text. Cover data extraction and form import through PDF save/reopen.

Co-authored-by: storybored379 <storybored379@users.noreply.github.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:47:41 -04:00
7aac682ef8 M7: Cut, Redact, Create Link and Recognize Text in the picture menu; keep the menu open (#1621)
* M7: add Cut, Redact, Create Link and Recognize Text to the picture menu; keep the menu open

The right-click menu of a picture in Edit text & images gains Cut Image,
Redact Image (a redaction mark over the picture), Create Link… and Recognize
Text… (for the picture's page), with translations in every catalog.

The menu was drawn only while the pointer was over the picture, so an item
outside it, which is most of a small picture's menu and Delete once the menu
reaches below the page, could not be clicked. An open menu is now drawn
wherever the pointer is.

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

* Update the catalog checksums in ATTRIBUTION.toml

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

* Fix the merged test file and checksums

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

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 03:47:20 -04:00
10ad9b46e5 Keep added text selectable over form fields (#1528)
* Keep added text selectable over form fields

Generated with Codex for @FredApps.

* Fix the content.rs test harness merge: harness_bytes and harness_with_step_dt share one builder

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-15 03:42:56 -04:00
b7976ef40e M12: show structure tag content (#1496)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:38:35 -04:00
980bd90087 Print: 4 x 6 in label paper and a Label layout for shipping labels (#1583)
* Print: 4 x 6 in label paper and a Label layout for shipping labels

Thermal label printers (e.g. VEVOR Y486) could not be used for eBay/carrier
labels: the paper list had no 4 x 6 in size, the chosen paper was never sent
to CUPS (the queue's default was used), and labels laid out sideways on a
Letter/A4 sheet printed small or rotated.

- PAPERS gains "4 x 6 in label (101.6 x 152.4 mm)"; cups_media() sends the
  chosen sheet to the spooler as `-o media=` (Letter, Legal, Tabloid, A3-A5
  and w288h432 for the label), so the driver feeds the size the PDF was
  laid out on.
- New Layout::Label: each page is cut to its printed area (a low-resolution
  render, as Fit Visible does), turned a quarter turn when it is sideways in
  the direction that makes its text upright (text_reads_up reads the text
  and form matrices), and fitted uniformly with a small margin. Vector
  content is kept, so barcodes stay sharp and undistorted. "Turn upside
  down" flips it 180 degrees. One label per sheet.
- Choosing the label paper selects the Label layout, and vice versa.
- Strings translated in every bundled catalog.

Tests: label geometry (crop, quarter turns, no stretching, margins),
cups_media, text direction; print, ui-egui and automation unit tests pass.

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

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Alessio Boschi <alessio.boschi@dinova.one>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 03:38:06 -04:00
24f93e6102 feat(ui): preference to hide author-only highlight hover previews (#1432) (#1466)
* feat(ui): preference to hide author-only highlight hover previews (#1432)

* Keep the Identity heading, translate the new hover preference, move its persist key out of json!

The heading rename dropped the existing Identity translation in every catalog; the
new checkbox label now has an entry in every catalog. The json! object in persist()
is at serde_json's recursion limit, so the new key is set after it like redact_prefs.

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

* cargo fmt

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

* Refresh catalog hashes after merging main

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:33:43 -04:00
a6124bcdfe M12: find untagged accessibility content (#1463)
* M12: find untagged accessibility content

* i18n: translate untagged content checker (rebased)

* fix(i18n): refresh untagged content translation hashes

* fix(tests): restore full test modules for find-untagged content

* fix(i18n): preserve existing translations after rebase

* ci: rerun shared timeout test

* chore(deps): refresh rust for the advisory gate

Use the current lockfile, including the rustls security update, so the dependency check can run on this branch.

* Refresh catalog hashes after merging main

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:33:19 -04:00