- Create PDF detects BigTIFF (II+/MM+) as well as classic TIFF; the tiff
decoder already reads it, so these scans now convert instead of being
refused.
- Image detection accepts only GIF87a/GIF89a, the headers conversion
decodes, so a malformed GIF header is no longer called an image and then
rejected with a misleading error.
- file-format-parity.md: XDP split into XFA data import/export (shipped
under form.xfa-data) and opening .xdp as a document (none).
Tests: bigtiff_images_become_pages; malformed GIF headers in
source_kind_tells_pdfs_images_and_text_apart. Both fail without the fix.
Claude-Session: https://claude.ai/code/session_01F8DFYWkDyTnEcin9PNkUW5
Co-authored-by: Claude <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Gates: fix two clippy failures on main
- ocr_ui: "Also recognize pages that already have text" used egui's own
checkbox, which clippy.toml disallows (#1184 landed beside #1173);
use widgets::checkbox like every other box.
- camera_edges: Rust 1.97's manual_range_contains; the rewrite keeps the
old answer for a NaN angle (false) with an is_finite check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019ojKsSCHWk2AuyDTFMHCd7
* M10: print from the web build through the browser's print dialog (gap 4)
The web build had no printer: spool::submit refuses on wasm, so Print
could only download the print-ready PDF. Now its Print dialog offers
"Browser's print dialog" (the default there), which reaches every
printer the operating system knows.
- web_print: PdfCraft draws the print-ready PDF's sheets itself
(engine Exporter), one sheet a frame since wasm has no threads:
300 dpi up to 100 sheets, 200 up to 300, 150 up to 1,000, at most
64 MP a sheet and 1 GiB of PNGs a job. The PNGs become blob URLs
and print from a hidden srcdoc iframe, one sheet per page, margin 0.
- Chromium prints every page at the default @page size, so a sheet of
the paper's size turned sideways is rotated 90 degrees CCW onto it
(as drivers do); other sizes get named @page rules.
- Copies, two-sided and colour are greyed out (the browser's dialog
sets them). If print() is refused (e.g. a sandboxed frame), the
print-ready PDF downloads instead, with the reason.
- pdfcraft-print stays free of web APIs; six new strings in all 20
catalogs (ATTRIBUTION hashes refreshed); docs and parity updated,
gap 4 closed.
Verified in headless Chromium against a trunk build: three sheets at
300 dpi, the landscape sheet turned, no blank trailing page, and the
download fallback. Firefox and Safari not yet tried.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019ojKsSCHWk2AuyDTFMHCd7
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
* Fix selected comment quick-bar color (#1191)
* M5: skip quick-bar recolour when commenting is forbidden (#1191)
The quick-bar colour swatch now checks allows_annotation() before
restyling the selected comment, matching the canvas and context-menu
gating. Without it, a restricted document showed a "Change comment
properties failed" toast on every swatch pick; the engine already
rejected the edit. Adds a regression test on an encrypted, print-only
fixture that fails without the guard.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01T3DWkcKZcZLbww1oeDbCfb
* M5: quick-bar colour also recolours a selected arrow (#1191)
Arrows are saved as Line annotations with an ending, so tool_for()
reports them as the Line tool and the Arrow tool's swatch never matched
the selected arrow. comments::made_by() treats the Line and Arrow tools
as the same kind. Adds a regression test that fails without the fix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01T3DWkcKZcZLbww1oeDbCfb
---------
Co-authored-by: Claude <[email protected]>
* M0: keep the camera edge test NaN-safe under clippy's manual_range_contains
Clippy 1.97 flags `d < 0.35 || d > PI - 0.35` in the camera edge hit test. Its suggested
`!(0.35..=PI - 0.35).contains(&d)` would count a NaN direction as a hit, which the two
comparisons did not, so the NaN check is kept explicitly. Behaviour is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011AsuqvnFXhY9ASL1bA2jtm
* M14: edits let go of the previous working file before writing the next (gap 13)
Every edit writes a new working file: the original plus one appended revision. The previous
one stayed alive in the working bytes, the display bytes and the renderer while the next was
written, so after a few edits a 1 GiB file peaked at three copies.
Each refresh path (full, comments/form, content, undo, redo, after save) now releases them
first: the working and display bytes fall back to the original (already held by the editor)
and the renderer is replaced by an empty one until the refresh rebuilds it. A refresh that
still fails after rollback draws the original instead of nothing. Save still writes from the
editor, never from these.
Measured on a synthetic 1 GiB file (release build, Linux): five edits peak at 2.1 GiB instead
of 3.1 GiB, in 8.5 s instead of 16 s. Saving still peaks at three copies; reading from disk on
demand stays blocked on the renderer (hayro parses one contiguous buffer, gap 2). gaps.md,
the parity notes and the roadmap record both.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011AsuqvnFXhY9ASL1bA2jtm
---------
Co-authored-by: Claude <[email protected]>
* Issue #1028: OCR can read pictures on pages that already have text
Recognize Text skipped every page with text, so a picture of text on a
page that also has real text never became searchable. The engine and
the ocr_recognize tool already had skip_text_pages, but the desktop
dialog never offered it, and turning it off read the page's own text
again and added it a second time, so search found every word twice.
- Engine: always collect the page's own glyph boxes; with
skip_text_pages false, a recognised word at least half covered by
them (an earlier OCR layer included) is left out. OcrPage counts
those words as already_on_page. The default still skips pages with
text.
- Dialog: "Also recognize pages that already have text", kept with the
other choices and used by Recognize text in multiple files too.
- Tools: ocr_recognize reports already_on_page; ocr_recognize_files
accepts skip_text_pages.
- The two new labels in the 15 catalogs that translate the OCR dialog.
- Synthetic tests (a picture of a sentence plus real text) for the
engine, the tool and the dialog; the engine test fails with the
filter turned off. Unit tests for the coverage check, including
NaN, infinite, inverted and overlapping boxes.
- parity, docs/roadmap.md M10 row and the ROADMAP.md progress log.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JqiZ658tGndQaHao5Q6mdm
* Issue #1028: Czech labels for the OCR text-pages option
The Czech catalog was completed (#1066) while #1028 was in review, and
czech_covers_ui_literals requires every tl! literal: the two new
Recognize Text labels were missing, so the test failed after the merge.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JqiZ658tGndQaHao5Q6mdm
* Contributors: credit rp6186030 as Raj Patel in the About window
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JqiZ658tGndQaHao5Q6mdm
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: rp6186030 <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>