Commit Graph
4 Commits
Author SHA1 Message Date
012b34a2a5 M4: rename colliding form field names when combining (organize.field-name-conflicts) (#1155)
* M4: rename colliding form field names when pages are imported or combined

Insert Pages from File, Combine Files and the grouped combine registered
incoming form fields under names the destination already used, silently
blurring them into one field (fields of one fully-qualified name are one
field, so their values mix). Importing now walks both sides' fully
qualified names (parent.T.child) — including names that hang only off
page widgets, which /AcroForm never lists — and renames a colliding
field's own /T to the first free variant (Text1-1, Text1-2, …), so its
children keep their place in the tree and the second of two colliding
incoming fields is renamed too.

A field_conflicts policy (rename by default; merge keeps the older
behaviour) and reporting — ImportOutcome::renamed_fields, the combine
outcomes, and a field_name_conflicts(dst, src) pre-scan — let callers
choose and prepare; engine, tool and Combine-tab wiring follow.

* M4: engine and tool wiring for renamed field names

Insert Pages from File carries a field_conflicts policy (rename by
default) and reports what it renamed: a repair-log note names each
old → new pair and warns that scripts referring to the old names were
not rewritten, and the undo step's label counts the renames, so nothing
is slipped in silently. Undo restores the original names, as ever.

The session's combine paths gain reporting variants —
combine_unlocked_reporting, combine_grouped_reporting — returning the
bytes and the renames, and doc_combine takes field_conflicts
("rename" | "merge") and lists renamed_fields [[old, new], …] in its
result when fields were renamed, so agents can find what their scripts
must call the field now.

* M4: the Combine tab warns before combining that field names collide

The Warnings column previews, per file, the form fields whose names
collide with earlier files' and will be renamed (Text1 → Text1-1, …),
computed once per list by a new in-order pre-scan (organize
combine_field_conflicts over the whole list, engine wrapper over the
tab's byte sources; a file that cannot be opened previews as nothing,
as its row already says why). The message is plural-aware and
translated in all sixteen catalogs.

* M4: parity for renamed field names on combine and insert

organize.field-name-conflicts ships: rename by fully-qualified name
(or merge, on request), reported everywhere it happens — repair log
and undo label on insert, doc_combine's result, the Combine tab's
warning column. The notes say what is still missing: the tab's preview
ignores narrowed page ranges, the UI offers no merge choice,
Create ▸ Multiple files renames silently, and scripts and XFA bindings
that reference old names are never rewritten.

* M4: review fixes for the field-name conflict work

Four findings from the PR's code review:

- Clippy: the -D warnings gate failed on the new engine tests (a
  format! with no interpolation, two drop(doc) no-ops on a
  reference); the branch could not pass its own CI.

- The Combine tab's preview warned that a file split into parts
  collides with itself: the preview made one source per row, while
  the combine groups parts of one file and imports it once. The
  preview now dedupes by group (each group's first entry stands for
  the file, as the combine does), and every part shows the file's
  preview.

- Collision detection compared /T bytes, so one name written in two
  encodings (PDFDocEncoding, UTF-16BE) did not collide — the exact
  silent blur this work exists to prevent — and renaming a UTF-16BE
  name rewrote it as from_utf8_lossy mojibake. Names are now compared,
  suffixed and written as decoded text (PdfString::to_text /
  PdfString::text), which is also how the forms crate reads them.

- The engine's combine_unlocked/combine_grouped duplicated their
  _reporting twins line for line; they now delegate with the Rename
  policy, as organize's own wrappers already did.

* Field-name conflicts: only Combine and Insert from File rename

import_pages (Duplicate, Replace) and extract_pages (Extract, Split) go back to Merge, so
copies of a document's own fields keep their names and a source with two same-named field
roots isn't split apart. Pasting pages into the document they were copied from also merges;
pasting into another document renames as Insert from File does. Regression tests for
duplicate, replace, extract/split (organize), duplicate (engine) and paste (ui-egui); the
PR's rename tests call import_pages_with(Rename) explicitly.

Merge fix-up: main made edit labels a translatable Label, so the rename suffix is now two
wordings in EDIT_LABELS ("Insert pages from {name} — 1 field renamed" / "— fields
renamed"), translated in all 20 catalogs. The Combine warning's @plural row is added to
el, kk, sl and tr. Catalog hashes refreshed.

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-11 21:18:21 -04:00
ade5dc4dcb Print: list driverless CUPS destinations, not only permanent queues (#658)
* Print: list driverless CUPS destinations, not only permanent queues

The Print dialog's printer list came from `lpstat -p -d`, which lists only
permanent queues. A driverless IPP printer discovered on the network — the
standard setup on modern Linux desktops — has no permanent queue: only
`lpstat -e` (CUPS >= 1.7) names it, and CUPS builds a temporary queue when
`lp` sends it a job. Until now such printers were invisible and the dialog
showed only Save as PDF.

Enumerate `lpstat -e` alongside `lpstat -p -d` and merge the answers: queues
first, deduplicated, default still from `-d`; a spooler without `-e` keeps
today's behaviour and loses only the driverless names. The .deb/.rpm now
recommend cups-client so a machine without a print stack is not silently
printerless.

Fixes #652

Co-authored-by: ZCode (GLM-5.3) <noreply@z.ai>

* Print: fix the off-unix build and a driverless default's marker

`lpstat_e_command` is called only from `printers`' unix block, so off unix
it was dead code and CI's Windows clippy gate (`-D warnings`) failed to
build the crate. Gate it like its caller.

The default destination can itself be driverless — `lpoptions -d` names a
destination no queue exists for — and the merge appended it from
`lpstat -e` with `default: false`, so the dialog preselected the first
queue instead of the user's default. The `-d` default now marks the
appended entry too; the test covers it, and CRLF in `-e` output.

Co-authored-by: ZCode (GLM-5.3) <noreply@z.ai>

---------

Co-authored-by: ZCode (GLM-5.3) <noreply@z.ai>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 22:39:36 -04:00
a2f0fb7363 Forms: fields listed only as page widgets join the form (#649)
A PDF whose AcroForm /Fields array is empty or missing listed no
fields, so none were fillable even though every widget sat on a page
with its name and value: Acrobat and the browsers fill those. The page
widgets are now adopted as fields — after the tree-listed ones, through
each widget's topmost unlisted /Parent so a field split across several
widgets stays one — and the leniency is recorded, never silent.

- forms: fields() walks /Fields as before, then adopts the unlisted
  page widgets; adopted_page_fields() counts them for the note.
- render: the inspector's field list adopts them the same way, so the
  Fields panel, the field-count notice and menu gating follow.
- engine: opening such a file notes "the form's /Fields list omits N
  fields; they were read from the page annotations" in the repair log
  (Document Properties ▸ Advanced).

Co-authored-by: GLM-5.3 <noreply@z.ai>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 21:43:09 -04:00
Eduardo SoaresandBrandon Thomas 49c6e2ec54 i18n: complete Brazilian Portuguese (pt-BR) interface catalog (#604)
* i18n: complete Brazilian Portuguese (pt-BR) interface catalog

Catalog by André Cardoso (andrecards) from #288, unioned with the
strings merged after the catalog was cut. Asset hash refreshed.

* i18n: translate the pt-BR strings added since the #288 catalog and gate coverage

* i18n: pt-br review fixes (line ending, redact/sanitize/page-boxes terminology)

* i18n: credit the pt-BR translator and document full coverage

Name André Cardoso (andrecards) as the pt-br catalog's translator in
ATTRIBUTION.toml: the ~1,826-entry gist posted in issue #288, with the
labels added since and the wording review as this change's contribution.
ATTRIBUTION.md is regenerated from it.

docs/localization.md moves Brazilian Portuguese from the menus-only
clause into the full-coverage list, states the 1,997-entry count next
to the provenance (following the Ukrainian paragraph's pattern), and
lists pt-br with the languages whose coverage tests gate every
command, All tools label and tl!() literal (added with the catalog).

people.toml is unchanged: PR #165's translator has no entry there, and
its policy is that only one's own entry may be added.

* i18n: pt-br writes '>' like the other Latin catalogs (Inter has no ▸)

* i18n: pt-BR final review fixes (toast grammar, contextual entries, menu gate)

* pt-BR: add the labels main gained while this catalog was completed (98) and drop stale OCR keys

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 20:56:13 -04:00