Commit Graph
38 Commits
Author SHA1 Message Date
aiongg 0af6cf2418 refactor(print): split lib.rs into error, settings, sheets, imposition
New modules: error.rs, settings.rs, sheets.rs, imposition.rs.
2026-10-26 22:18:50 +00:00
aiongg ce684cd1d3 Merge track r3-t06 2026-10-24 06:50:31 +00:00
aiongg 8ff6c8af31 Merge PR #2748: M10: a booklet decides two-sided itself, as Acrobat does (#2707)
Conflicts: crates/automation/src/printing.rs: kept job_duplex (#2748) next to bad; #4161's paper_named/spool_job unchanged.
2026-10-24 06:22:34 +00:00
aiongg 496e3b3308 Merge PR #4312: M10: preserve page artwork when printing annotations 2026-10-24 06:22:19 +00:00
aiongg 0041a3b10e Merge PR #4161: M10: every paper size reaches the printer, from the dialog and doc_print (#3752)
Conflicts: ROADMAP.md: kept both progress-log lines.
2026-10-24 06:22:15 +00:00
mtlcl410andClaude Opus 5.5 827cec3c6e M2: resolve an indirect annotation /Subtype before the Invisible print filter
The print filter read /Subtype with a direct-only lookup, so a standard
annotation whose /Subtype is an indirect reference (ISO 32000-2 §7.3.10)
looked like an unknown type and an Invisible flag dropped it from print
(Table 167 applies only to non-standard types, Table 171). The subtype is now
resolved through the document first.

Also removes a stray cell separator in the hayro-interpret row of
vendor/README.md that pushed the test references for patches (44)-(56) out
of the table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 12:34:44 +08:00
mtlcl410andClaude Opus 5.5 a6df1d3ca5 M2: Invisible annotations of unknown types and popups don't paint their appearance on the page (ISO 32000-2 Table 167)
An annotation whose Invisible flag (bit 1) is set and whose /Subtype is none
of the standard types of ISO 32000-2 Table 171 is no longer drawn (Table 167)
or printed (Table 167: "do not print it even if the Print flag is set"). A
/Popup annotation's /AP is no longer painted as page content: per §12.5.6.14 a
popup has no appearance stream of its own and is shown by the viewer as a
window for its parent. Printing already left popups out.

Link appearances are painted as before (§12.5.5).

hayro-interpret patch (56), recorded in vendor/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 12:06:49 +08:00
mtlcl410andClaude Opus 5.5 184e9f30ff M10: a booklet decides two-sided itself, as Acrobat does (#2707)
dialog hides Two-sided while Booklet is chosen and prints the booklet two-sided itself
(Using Acrobat X Pro, Print dialog box options: "Acrobat automatically enables duplex
printing, if available, for booklet printing"; later manuals and the current help no
longer say so, and on a current Acrobat the Two-sided control is shown for Size and
hidden for Booklet with the same duplex-capable printer). print::booklet_duplex gives a
booklet's setting: both sides flip on the short edge, the fold of a 2-up sheet turned
sideways; front or back only is one pass of a manual duplex and prints one-sided. The
Print dialog hides Two-sided while Booklet is chosen and sends that setting, leaving the
Two-sided choice as it was for the other layouts (and as #2001 remembers it); doc_print
applies the same rule. This replaces the first version, which rewrote Two-sided to the
short edge when Booklet was chosen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 09:27:26 +08:00
Kyle Kassen a8839bc5b1 M10: preserve page artwork when printing annotations 2026-10-23 16:37:10 -05:00
Dylan M. Taylor de91632934 Merge pull request #4151 from aiongg/fix/coderabbit-c2-edit-organize
fix(edit,organize,create): bound the work hostile files cause in editing, page organizing and image import
2026-10-22 21:57:13 -04:00
Juan Jurado 635b42d989 M10: ARCH E1 goes out as PWG 5101.1's na_wide-format_30x42in (#3752)
30 x 42 in is `na_wide-format_30x42in` in PWG 5101.1-2023 and the IANA
IPP registry (checked against both). `na_arch-e1_30x42in` was asked for
on the IPP list in 2021 but never registered. CUPS maps the new name to
the PPD's 30 x 42 in size (cupsfilter, synthetic PPD), and the paper
test pins it and fails on the old name.

Doc comments for bad, printers and two paper tests, for the review's
docstring check.

Review: storytold/pdfcraft#4161 (CodeRabbit).
2026-10-22 18:42:49 -06:00
Juan Jurado 7594713937 M10: spell the ARCH media names as PWG 5101.1 does (#3752)
ARCH A-E went out as arch_a_9x12in .. arch_e_36x48in; PWG 5101.1 names
them na_arch-a_9x12in .. na_arch-e_36x48in (ARCH E1's IANA-registered
na_arch-e1_30x42in already had the prefix). CUPS parses either from the
dimensions, which is why the cupsfilter check passed, but a driverless
printer may accept only the names it advertises. Re-checked the corrected
names with cupsfilter against the synthetic PPD.

The paper test now pins every size's exact name, and fails on the old
spelling. Doc comments for doc_print and PrintDraft::job.

Review: storytold/pdfcraft#4161 (CodeRabbit).
2026-10-22 18:24:54 -06:00
Juan Jurado 5f042c691f M10: every paper size reaches the printer, from the dialog and doc_print (#3752)
A CUPS job that names no paper gets the queue's PPD default (A4 on the
reporter's Xerox queue), whatever the sheets were laid out on. Two gaps
remained after #1583:

- cups_media had no name for 13 of the 21 PAPERS sizes (A6, ARCH A-E1,
  ANSI C-E, A0-A2), so the Print dialog sent no media for them. They now
  use PWG 5101.1 names (the IANA-registered na_arch-e1_30x42in for ARCH
  E1), which carry their dimensions: CUPS feeds the driver's size of those
  dimensions whatever the driver calls it, and driverless IPP printers use
  them natively. Checked with cupsfilter against a synthetic PPD whose
  sizes use vendor keywords (ARCHD, AnsiD, A0...): each name produced its
  size's bounding box. The 8 sizes that already worked keep their PPD
  keywords.
- doc_print to a printer (CLI run, MCP) sent no paper at all. It now
  resolves the paper to its PAPERS index and sends the same media option
  as the dialog, after the driver options. The job is built in spool_job
  so it can be tested without printing.

print::media_option is the one place both get the option from. Both new
tests were seen failing without the fix. The macOS tray half of #3752
(Auto Select instead of the PPD's default tray) is a craft-print change.
2026-10-22 18:06:49 -06:00
aiongg 2430d53f9e fix(print): read the text after a form the budget refuses 2026-10-22 23:37:07 +00:00
aiongg 0e643d45a0 Merge #4114 (fix/coderabbit-b-shared) into fix/coderabbit-c4-core
#4114 moves the bodies of Stream::decoded_within and decoded_strict into
decoded_within_typed and decoded_strict_within_typed, which return
FilterError so DecodeBudget can tell a spent limit from corrupt data.
This branch checks /Filter with readable_filters there. Kept #4114's
typed functions and made readable_filters return
FilterError::Unsupported, so a /Filter that is not a name fails both
typed decodes without charging the budget; decoded_first_filter_strict
maps it to CosError as before.
2026-10-22 20:30:25 +00:00
aiongg a72845ad80 Merge upstream/main into fix/coderabbit-b-shared
Redaction reads page content and form XObjects strictly (upstream) and
within one page's decode budget (this branch): both now go through
DecodeBudget::decode_strict.
2026-10-22 19:44:33 +00:00
aiongg 24701fe959 fix(print): list the source pages once per imposition
page_form listed every page of the document to take the one it
printed, so imposing P pages walked the page tree and copied its leaf
dictionaries P times over: P^2 page copies. impose passes the list it
already holds. A page index outside it is an error, not a panic.
2026-10-22 17:43:04 +00:00
aiongg 77480498b2 fix(engine): low-resolution printing prints only the chosen content
Pages printed as pictures were drawn with everything on them, so form fields only
printed the page content, and the document with stamps printed every comment. The
pictures are drawn from a copy holding only what the vector print draws.
2026-10-22 13:18:26 -04:00
aiongg 8c8f56a2e2 fix(print): page content and label forms share one decode budget per page
Printing and the label text-direction check decoded every /Contents
entry with a fresh 1 GiB allowance, and the direction check re-entered
forms at every Do with only a depth limit, so a few hundred bytes of
nested repeated draws meant hundreds of thousands of form walks. Both
charge a per-page DecodeBudget: printing refuses a page whose content
runs past it, and the direction check stops reading there.
2026-10-22 16:57:54 +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
Ahmad Mujahid 69109cd43e fix(print): reuse shared appearance promotions (#3532) 2026-10-20 15:51:16 +04:00
Ahmad Mujahid 1489436392 fix(print): preserve existing appearances without Subtype (#3532) 2026-10-20 14:34:40 +04:00
Christoph OrtenburgandClaude Opus 5.5 0857996202 fix(print): offer the US #10 envelope as a paper size (#2734)
4.125 × 9.5 in (297 × 684 pt) in the print dialog's Paper list and
doc_print, fed to CUPS as Env10. Appended after the 4 × 6 label so
LABEL_PAPER keeps its index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:19:54 -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
8e803b6290 Print: print as image (Advanced), with grayscale pictures (#1965)
* Print: print as image (Advanced), with grayscale pictures

print::Settings.raster (dpi 72-600, grayscale): Session::print_pdf renders each
distinct page used once the way the viewer shows it (comments left out when the
print leaves them out), composes it over white, grey by Rec. 601 luma when
grayscale is on, makes an image-only document of the pictures and imposes it on
the paper like the pages themselves. The Print dialog's new Advanced section
(folded unless on) offers it at 150, 300 or 600 dpi; doc_print takes as_image
and dpi. Rows in every language; parity print.print-as-image shipped,
print.grayscale shipped (the pictures are grey, not only the printer asked).

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

* ATTRIBUTION.toml: the language files' checksums after the new rows

cargo xtask assets checks each catalog's sha256; the rows this branch adds
changed the files without updating it.

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

* Refresh catalog hashes after merging main

---------

Co-authored-by: Osvaldo Duran <338149330+regendorf-ux@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:34:09 -04:00
Marco Arnulfo 9c2fc4f8e9 M10: A6 paper in the print dialog and doc_print (fixes #1838) (#1849) 2026-10-15 02:26:16 -04:00
Alexis e75082c9d6 Printing: a layer that is off in the document stays off on the sheets (#1135)
The print-ready PDF that `pdfcraft_print::impose` builds starts from a new
catalog with only /Type and /Pages, so the document's /OCProperties was not
carried over. The pages' marked content still refers to its optional-content
groups, but nothing said a group was off, so a layer that is switched off in
the document (and not drawn on screen) was printed anyway.

Keep /OCProperties in the new catalog, so the sheets hide the same layers the
document hides. Test: a one-page document whose red square is in a layer; with
the layer off by default it is not drawn on screen and, now, not on the
printed sheet; with the layer on it is drawn in both.

Printing still follows the layers' default state only; applying the Print
event of /AS (a layer shown only on screen or only in print) is not done here.
2026-10-15 02:16:23 -04:00
dbef872ed7 M2: preserve precision when writing page transforms (#946)
Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
2026-10-15 02:16:13 -04:00
Filip PranklinandClaude Opus 5.5 4f375bf45c fix(print): custom pages-per-sheet counts no longer overfill sheets (#1336) (#1430)
Layout::multiple(n) maps counts without a fixed grid to a near-square
grid (3 -> 2x2, 5 -> 3x2), but Layout::Multiple kept only the grid, so
layout() filled every cell: three per sheet put four pages on a sheet.

Layout::Multiple now carries per_sheet, capped by the grid; spare cells
stay blank and cut-stack piles use the same count. The doc_print tool
and the print dialog pass the count through.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:41:45 -04:00
Marco Arnulfo 2796f3de0d fix(print): refuse huge posters at the sheet limit instead of overflowing (#1335) (#1376)
A poster scale large enough to make both tile counts saturate at
usize::MAX overflowed when they were multiplied for the 1024-sheet
check: a panic with overflow checks, and a product that wraps to 1
without them, which let the sheet loops run. The headless doc_print
tool accepts any finite positive scale, so a script could reach it.

Multiply with checked_mul and treat an overflow as over the limit, so
the request gets the existing "more than 1024 sheets" error.

Adds tests::huge_posters_are_refused_at_the_sheet_limit, which panics
before this change and passes with and without overflow checks after it.
2026-10-12 06:27:50 -04:00
513a1ecc3a i18n: let the layers below hand their sentences over, so they can be translated (#478)
* i18n: let the layers below hand their sentences over, so they can be translated

The accessibility findings, the PDF/A problems, the signature details, the
page-range mistakes and the edit labels were English in all ten languages,
because matching their wording from the UI with a tl! literal would break
silently whenever the engine changed it.

So each layer now hands the wording over instead, apart from the document's
own words: a WORDINGS list of every sentence it can say, with holes in it, and
said(&words), which looks the wording up through the closure it is given and
fills the holes in a single pass. in_english changes nothing, for the CLI and
the tests; the UI passes &i18n::words, a catalog lookup. A wording with no
entry comes back as it stands, which is the English, so the nine catalogs this
does not touch read exactly as before.

a11y::report_html takes a language tag and a words closure, so a saved report
reads the way the panel read, down to its lang attribute. print::PrintError
gained a variant per thing a person can be wrong about, with the numbers apart
from the words; faults rather than mistakes keep their own text.

Spanish translates all of it (107 entries). spanish_covers_what_the_engine_says
requires an entry per wording in the eight lists and
spanish_keeps_the_holes_in_what_the_engine_says requires the holes to survive
the translation, so the catalog cannot fall behind the engine unnoticed.

No new tl! literal, so the Chinese, French and Russian UI-literal gates are
unaffected.

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

* Sign tests from #1099: compare against the &'static str kinds

* Camera tests: undo returns a Label now

---------

Co-authored-by: Fernando Areán <soporte@codebit.es>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 15:45:51 -04:00
durtyrobbinandBrandon Thomas f036d3841b Print: architectural paper presets and PDF scale controls (#599 follow-up) (#1123)
* M10.5: Restore architectural paper presets and PDF scale controls

Port the missing paper catalog and scale controls from PR #599 onto the merged Windows printing implementation. Keep the spooler and background discovery from #847/#1007. Document the feature-by-feature audit and verification limits.

Assisted-by: OpenAI Codex

* Drop the provenance write-up and the screenshot example (the kittest covers the dialog)

* Refresh catalog hashes

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 14:49:32 -04:00
MarekSpodBiedry 2a909f361c Print: a scaled annotation appearance no longer prints larger than its rectangle (#802) (#861) 2026-10-10 15:50:20 -04:00
a9209c3235 M10: select pages in the Pages panel and print the selection (#296)
* M10: select pages in the Pages panel and print the selection

The Pages panel could only jump to a page. It now picks several:
Ctrl/Cmd-click toggles a page (the first one keeps the current page
selected too), Shift-click takes a range from the last clicked page, a
plain click goes to the page and starts over, Escape clears. Picking does
not turn the page. It is the selection the Organize grid and the page
commands already use; the click rules moved to DocView::click_page and
both places call it.

Print opens on a new "Selected pages" choice when pages are picked and
prints them in page order. They are passed as positions
(print::select_listed), not as a typed range, which would read numbers as
page labels first. ui.state reports active.selected_pages.

Windows still has no printer output, so there the result is a PDF of the
selected pages. No context menu or drag in the panel.

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

* Restore ROADMAP.md, emptied by accident in this PR

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-08 12:00:35 -04:00
AyanandBrandon Thomas 2f872f7e7d M10.5: add cut-and-stack page order for single-sided printing (#197)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-07 21:38:21 -04:00
Brandon ThomasandClaude Opus 5.5 d3db99818f Rename PrintCraft to PdfCraft (#174)
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now
PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the
PrintCraft settings and crash-recovery folders to the new name, and both apps read settings
saved under the old storage key, so upgrades keep recent files, preferences and unsaved work.
The signing test fixtures keep their PrintCraft subject names (they are binary).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:03:57 -04:00
Brandon ThomasandClaude Opus 5.5 1434db626a Never crash: deny the panic lints in crates that are already clean
annot, compare, content, cos, export, geom, model, ocr, preflight, print and redact have no
production `unwrap`/`expect`/`panic!`/`unreachable!`/`todo!`/`unimplemented!`. The attribute
keeps it that way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:12:21 -04:00
Brandon ThomasandClaude Opus 5.5 2aa29254da M10.5: Print: new print crate (L4) imposes the pages to print onto sheets (Fit/Actual/Shrink/Custom, Multiple pages per sheet with order, borders and auto-rotate, Booklet, Poster with overlap and cut marks, orientation, Comments & Forms honouring the Print flag) as a fresh print-ready PDF; page ranges with labels, odd/even, reverse; CUPS spooler (printers, copies, collation, duplex, monochrome). Print dialog (⌘P) with a live sheet preview from page thumbnails and Save as PDF; print permission enforced; printers/doc_print tools (61 tools)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:40:57 -04:00