Refs #5989. Ctrl/Cmd+A selects every object on the page; copied objects paste as page content that draws the same, within a document or into another, with organize's bounded deep copy across documents. Headless: object_copy, object_paste, object_move all.
* M9: redraw named-layer signature status layers
- /N drawing one Form that holds n1 and n3: n1, n3 and n4 redrawn for the state
- copies made once per original and state; originals never edited
- Lucide icons; captions in Helvetica, embedded Inter, or craft-fonts outlines
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: draw signature states in the display copy, exports and print
- marks follow the bytes shown; a changed verdict redraws after a metadata edit
- background opens pick up the trust and captions current at commit
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: status captions in the interface language
- the three captions in all 22 catalogues
- Signatures panel colours from sign::status_rgb
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: signature badge in the widget's corner
- a quarter of the shorter side, 48pt cap; outline only under 32pt
- dropped for a drawn valid state; bottom-right where the appearance draws one
- sign_list reports verdict_shown; parity and roadmap
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: keep Arabic signature captions in logical order
Pass raw RTL catalogue text to the signature appearance shaper so UAX #9 is applied exactly once. This keeps the rendered word order and exported /ActualText consistent.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M2: read /ActualText over painted paths and pictures
- A replacement that marks no glyph reads where it paints, cut to what is visible and along the way it is drawn.
- A span the page content leaves open ends before the annotations.
- OCR and scan field detection still count such a page as having no text of its own.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M8: redaction strips /ActualText around paint it clips
- A path or shading written again under a clip counts as changed, so a partly redacted word drawn as outlines leaves no name in the file.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: recognise only layer forms that draw n1 and n3
- Settle on the one form holding both first, then read its content once per call.
- Skip an /N that is its own layer form; copies keep the layer's /OC.
- A verdict counts as drawn when any visible widget shows it.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: keep the signature badge and redraw wherever a state changes
- The badge stays for every state; its mark sits bottom-right as the widget lies on the page.
- An edit that changes a verdict redraws every page; captions apply before startup files open.
- Print, the CLI's render and the repair log follow the display copy.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: restore the attribution usage the rebase dropped
- Usage lines for the three icons, Inter and the craft-fonts input; ATTRIBUTION.md rebuilt from them.
- Roadmap and localization notes for this round.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: pin signature field names on a nested form
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: bound the signature field walk
- kids share their parent's name and type instead of each copying them
- a widget stays listed under every field that claims it, as one shared copy
- a field written in place in a shared /Kids array is walked once
- caps of 20,000 fields, 100,000 entries and 4096-byte names; no states drawn past them
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: keep a document signed when the field walk stops early
- list_cached adds a signed row of unknown validity for the fields the walk didn't read
- Save As and actions then append, and Optimize, Sanitize and full rewrites are refused, as for any signed file
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M8: removing hidden content no longer strips the names of shadings
- With no regions to clip, a shading is left alone instead of wrapped and marked clipped
- clean_replacements skips scopes already marked, so deep nesting stays linear
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M2: read a picture's /ActualText over the whole picture
- draw_image's transform maps pixels, not a unit square, so the text sat on one pixel in the corner; the test now draws a 2x2 picture
- Painted grows in place instead of being copied through an Option
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: cap the bytes of names the signature field walk keeps
- A kid's name repeats its parent's, so one long name over 20,000 kids came to 80 MB; the walk now stops at 4 MiB of names and counts the rest as unread
- Cover the widget written into a shared /Kids array and the field cap among kids, which no test caught
- sig_fields is private again, and the cap docs say what happens past each cap
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: skip status-layer work that changes nothing; fix tests that could not fail
- show_status_layers and has_status_layers return before reading the form when nothing is signed, so an unsigned form no longer walks every field on each edit
- revalidate and draw_captions leave documents with no drawn state alone, and no longer copy the signature list to update it
- Scan form detection reads the page's text once; comment and badge tips share one hover rule
- Tests: drop a table row that repeated another, stop taking an expectation from the code under test, stop comparing an Arc with itself, give bare asserts messages
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: test the signature field name cap at the engine boundary
- A 4,096-byte name is read and blocks a full rewrite; at 4,097 bytes the list holds the unread-fields row and a full rewrite is still refused
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: test signature states beside fields the viewer draws
- One display copy holds both a field's drawn appearance and the signature's state.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: draw Thai and Devanagari status captions in their own script
- script_of names Thai and Devanagari, so a craft-fonts face meant for either draws the caption as outlines; without one it stays English.
- Localization, parity and the craft-fonts usage line list the new scripts.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M2: paint left out by an exclusion mixes its /ActualText replacement
- A replacement placed by outlines or pictures, some of them excluded, fails filtered extraction as one with excluded glyphs does.
- Glyphs still place a replacement that has them.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: a long-named field that can't hold a signature leaves the walk whole
- A leaf of another type passed over for its name is no signature field and has none below it, so an unsigned form stays unsigned.
- A signature field, or any field with kids, still counts as unread.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: status captions use document faces only
- Each script takes the face PdfCraft writes it into documents with, so Chinese stays English although the pinned craft-fonts' interface-only Chinese face is meant for Hani.
- The craft-fonts usage line names Devanagari: the pinned revision has a face for it, and Hindi captions are drawn in it.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: refuse a new signature field the field walk can't read back
- Signing adds a field only when the walk reads the whole form with it: past a cap the signature would be listed as unread fields, and its name could repeat an unread field's.
- A field the walk already reads is signed as before.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: refuse a new signature field with a blank name
- The walk lists no field without a name, so signing now also requires the walk to list the new field: one named only with spaces or NULs would be a signature nothing shows.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: an unnamed signed field or an unreadable form counts as unread
- A signed field with no usable /T, or an /AcroForm or /Fields of the wrong type, cuts the field walk short: the document keeps its unread-fields row and stays signed.
- Signing refuses a blank new field name before it adds the field, so the refusal still says why.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: no display copy when no signature state is drawn
- display_bytes_for makes a copy only when it added an appearance or drew a state, as the app does.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: the verdict mark stays on the page
- A widget past the crop box takes its mark in the part on the page, or none when too little of it is there.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: typed values stay drawn while a signature redraws every page
- A form edit that changes a signature's state puts its values back in unbaked after the pages are invalidated.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: the action, Save As and shading tests check their effects
- The signed action and Save As tests reopen the output for the title and the comment; the shading test checks its clip.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: values typed earlier stay drawn while a signature redraws every page
- A form edit that changes a signature's state keeps every value still waiting on its page, not only its own.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: comment and content edits keep typed values drawn too
- An edit to one page's comments or content that changes a signature's state moves no page, so the values still waiting on their pages stay pending, as after a form edit.
- The pending-value test runs a field deletion and a comment after a fill, under a certification that allows the fill; its messages say what it checks.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: the language-change test skips without catalogs
- Like the other language tests, it returns early when the build has no craft-i18n catalogs.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: signed fields behind kids without a name count
- A kid with no /T but kids of its own is walked as a field node, so a signed field under it is listed by its full name.
- An unnamed kid claiming a signature its field doesn't have cuts the walk short, so the document stays signed and keeps its unread-fields row.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
---------
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
page_add_text and content_update took only RGB, so text added to a CMYK
print file painted 0 0 0 rg, rich black on press. They now also take gray
(0 to 1) or cmyk ([c, m, y, k]), painted with g or k and kept in /PCAdded as
/Gray or /CMYK beside /Color, which holds the matching look. An ink applies
only while the colour still shows its look: picking another colour, also in
the Format panel, drops it for good. RGB text is written byte for byte as
before, and a stored ink that doesn't match its colour is ignored.
* Fill & Sign: a Border control for typed text, and a border colour of its own
Rebased onto main as one commit: main's Fill & Sign text colour (#2444) runs through the
same places as the border (the open box, typed(), the page input, the toolbar and the
preferences), so every step of the branch met it again. Text colour and border now travel
together as TypedLook (typed_with), which keeps typed() and the new function under Clippy's
argument limit; a border without its own colour takes the text's. Make Current Properties
Default on typed text also keeps its colour for new text now.
The branch's commits, in order:
- Typewriter text: a border change grows its box on every side, so the text keeps its place
- Fill & Sign: a Border control for typed text and dates
- Parity: the typewriter border's evidence
- i18n: the Border label in every catalog, recorded in ATTRIBUTION.toml
- Text boxes, typewriter text and callouts: a border colour of their own
- Fill & Sign: the toolbar follows the selected text; a Border colour button
- fill_sign_add and comment_edit take `border_color`
- Parity: the border colour and the toolbar following the selection
- i18n: Border colour and Same as text in every catalog, recorded in ATTRIBUTION.toml
- Typed text's border: /Border's width is read, a non-finite border colour refused, a saved Same as text restored
- Fill & Sign: the toolbar changes only the selected text; Make Current Properties Default keeps new text's border
- Review: Make Properties Default gives a text box's or callout's tool its border colour
- i18n: the new labels in the Persian catalog, recorded in ATTRIBUTION.toml
- Review: a test that dragging the Font size control resizes the selected text to where the drag ends
- Rebase: the typed text's border in main's annot and comments modules (Style's border colour, exports), and craft_ui's ComboBox
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M5: text boxes and typed text take an oval or a circle for their border
Rebased onto #4146's new head as one commit (see there: main's Fill & Sign text colour runs
through the same places). The border shape goes into TypedLook's border with the width and
colour; a circle around typed text still takes the snug box.
The branch's commits, in order:
- Text boxes and typed text take an oval or a circle for their border
- A circle goes snug around its text
- Review: fill_sign_add takes an oval or a circle only with a border
- Review: a circle made on a quarter-turned page is sized from the text as shown
- Review: Border ▸ None takes an oval or a circle away with the line
- Review: the Border colour button shows the selected text box's own colour
- i18n: Border shape in the Persian catalog, recorded in ATTRIBUTION.toml
- Review: Border ▸ None on a circled text is one undo step
- Rebase: the border shape in main's annot, engine and comments modules; craft_ui's ComboBox; Fill & Sign's border controls in a function of their own (main's complexity limit)
- Review: Thickness 0 in Comment Properties takes an oval or a circle away, as Border ▸ None does
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Open the Font size popup first in the drag test: main moved the size into a popup
* Border shape per selected comment, and split the oval/circle border out of draw_free_text (clippy cognitive complexity)
* Re-run CI (the push of the main merge did not start it)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Comments copy and paste, within and between documents. ⌘C with comments selected (and no text
selected) copies them, each with its replies, as XFDF: into the app's clipboard (every tab and
window) and as text onto the system clipboard. ⌘V, Edit ▸ Paste comments or the page's context
menu pastes them onto the current page at their own positions, as new comments under names of
their own, so nothing is replaced and pasting twice gives two sets; the copies are selected.
XFDF with comments from the system clipboard pastes too, so comments travel between processes.
pdfcraft_xfdf::export_xfdf_comments writes a selection with its replies; import_comments takes
a target page and a name prefix and imports copies (the usual import still replaces same names).
Engine: Edit::PasteComments { xfdf, page } (one undo step, comment permission) and
Session::export_comments. Tools: comments_copy (read-only) and comments_paste.
Several comments select at once: ⌘-click (Ctrl on Windows and Linux) or ⇧-click adds one or
takes it out, on the page and in the Comments panel; the one clicked last has the handles.
Delete removes them all as one step, a drag moves them together, the context menu copies them.
Connected lines, polygons and clouds finish from the right-click menu's Complete (with Undo last
point and Cancel drawing), as other editors do, besides Enter and a double-click.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Merge origin/main. Conflicts: docs/changelog headers keep both histories;
edit/run.rs keeps EnhanceScan beside main's run_edit_page_image; optimize
images.rs keeps main's scaled() and the crate-visible jpeg(); the German
strings in ui-egui/src/i18n/de.tsv go with that file, which main moved to
craft-i18n. decode_samples now takes main's ImageBudget; a scan clean-up
run shares one sized by the file.
Review (CodeRabbit, scan_clean.rs): a page whose own images and a
scan-holding Form only together cover it now cleans the images and reports
the Form; a page whose images alone cover it reports its Form too. New
regression fails on the old classification.
Copying selected page text now also puts an HTML fragment on the system
clipboard, so a paste into a word processor or mail client keeps
paragraphs, bold, italic, size, colour and font family. The plain text
is unchanged and stays on the clipboard as the alternative.
- export: clip.rs, the clipboard model (ClipText of paragraphs of
styled spans), its builder with caps (20 000 spans, 2 MiB of text,
16 MiB of HTML), font_family (subset tag and style suffixes off,
camel case spaced) and clip_html. Family names are reduced to
letters, digits, space, '-', '_' and '.', so a font name cannot leave
the CSS string; text goes through esc, which drops control
characters.
- engine: Document::rich_copy(page, text, glyphs). Glyphs (view space)
take the style of the reading block their centre lies in; blocks are
style-homogeneous. A line wrap inside a block is a space (none after
a hyphen), a change of block a new paragraph. Refused when the
document does not allow copying. compare.rs's display unit and
weight/slant inference are shared, not duplicated.
- automation: text_extract takes format "text" (default) or "html";
html adds each page's html and styles_incomplete, for the whole page
or only what rect takes. Other values are InvalidArgs.
- ui-egui: Copy of a run or column selection writes HTML plus the
plain text in one arboard set (desktop only); the web build and
Select All of the whole document stay plain text. last_copied_html
records what was produced, for tests.
- parity: view.copy-with-layout is partial; notes list what is missing
(RTF, underline/strike-through, tables, Select All, web, glyphs
outside reading blocks).
Formatting inference and the HTML serialiser are adapted from pdfcer
(MIT, same author) at 540b00ef1a405d1baa67c92f3858535037c7cde8.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
bookmark_export returns an outline, or one bookmark's subtree, as JSON:
titles, nesting, open state, colour, bold/italic and destinations as page
plus view in PDF user space (ISO 32000-2 12.3.2.2), optionally written to
a file. bookmark_import takes it back, inline or from that file, under any
bookmark at any position or in place of the outline, as one undoable edit;
bookmark_copy does the same from another open document by page number.
A bookmark whose page the target lacks keeps its place without a
destination and is reported in `unmapped`. Every node is checked before
anything is written (title, page, colour, view type and arity, 33 levels,
100,000 bookmarks), and the error names the node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
pdfcraft-annot reads a page's 3D annotations and the model each shows:
the /3DD stream, or the stream a /3DRef names (at most four deep, so a
cycle ends), decoded within 256 MB and typed PRC or U3D by /Subtype.
pdfcraft-engine exposes both on Document.
The automation tool model3d_render picks a page's Nth 3D model, parses
PRC with pdfcraft-prc, assembles and frames it with pdfcraft-prc-render
from a chosen direction (default an isometric-style view, Z up), and
returns the image as a PNG. U3D gives an error until a decoder lands.
parity misc.3d-view moves to partial.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
A new `model_add` automation tool and `Edit::AddModel3d` embed a U3D or
PRC file as a 3D annotation (ISO 32000-2 13.6.2) in a chosen rectangle:
- The model goes into a Flate-compressed 3D stream (13.6.3) with /Type /3D
and /Subtype U3D or PRC, read from the file's header (`U3D` and a zero
byte, or `PRC`); anything else is refused before the document changes.
Models over 256 MiB (MAX_MODEL_BYTES) are refused. model_add checks the
file's size (from the file system) and its header before reading the
rest, so a large file that is not a model is never loaded; the path goes
through the read root, and `read_model` joins the helpers the
file-access test knows take a resolved path.
- /3DD references the stream, so the annotation has its own instance.
- /3DA states /A: click (XA, the standard's default and the tool's),
page_open (PO) or page_visible (PV).
- /AP /N is the poster readers print and show before activation: a chosen
image or PDF page fitted inside the box keeping its shape and centred (the
custom-stamp picture path: `mark_source`, `fit_picture`, and the stamp's
placement matrix now shared as `appearance::picture_place`), or, with no
picture, a light frame labelled "3D model". It is drawn in user space and
not counter-rotated, so on a page with /Rotate it turns with the page, as
the model does.
- /Contents carries the name (the file stem by default) as the alternate
description (12.5.2).
- A document older than the model needs gets /Version in its catalog
(7.7.2): 1.6 for U3D, 1.7 for PRC, as the optimizer does for JBIG2.
Adobe's extension level for PRC in 1.7 is not written.
- The 3D rectangle has its own MAX_MODEL_SIDE (14400 pt).
The edit is undoable ("Add 3D model"), needs the comments permission and
rebuilds only the comments view. A new parity entry, misc.3d-add, is
partial: no poster rendered from the model, no views (/VA, /3DV), no
replacing the poster, no UI command, no /Extensions. misc.3d-view is
unchanged. ROADMAP.md progress log line.
Adapted from pdfcer (MIT, same author), commit 1ca5ece, rewritten to this
repository's types and rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Reuse the existing subset-tag parser through the engine facade and share the fallback formatting state.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
The four character match blocks become two const tables in a new engine
module, text_script, keeping the minus sign's mapping. One toggle_script in
Edit text replaces the three copies (paragraph editor, added-text draft and
the Format text button state).
A range is now 'already in the script' when at least one character is and
none of the others has a form in it. Before, one character with no Unicode
form (a space, q, H in H2O) kept the range from ever counting as converted,
so the second click converted again, changed nothing, and the text could not
be toggled back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Resolve all 13 merge conflicts between upstream/main and main
- Preserve full image editing suite: OBB, crop, proportional resize, arrange, and context menu actions
- Preserve text editing formatting tools: bullet/numbered lists, subscript/superscript, and line spacing
- Cleanly integrate upstream's canvas modularization, font updates, and styling changes
- Refactor arrange_image and run_edit_page_image helpers to satisfy clippy cognitive complexity limits
- All engine, edit, and UI integration test suites passing cleanly
Includes #4325#1952#4180#2529#4320#4805, preserving their original histories.
Keep paragraph alignment and hard/soft wrap metadata together; retain CCITT/downsampling/RGB ownership guards with bounded JBIG2 export and exact-copy symbol encoding.
Combined interaction regressions added. Validation pending.
Moving a rectangle, an oval or any other comment showed only its blue
frame going, the shape staying where it was until release. The comment is
now rendered alone once as the drag starts (Document::comment_layer: a
read-only copy with only that comment, no page content) and laid over the
page where it goes; the page beneath keeps showing it where it was until
the move is applied, as a CAD program's move does. Rendering the page
without it would cost a whole page's render at every drag on a heavy plan.
Stamps and Fill & Sign marks keep their own layers. annot: isolate_annotations
for several comments of a page, so a group can be drawn together.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The preview cloned and wrote the whole document on the UI thread for every
page it stepped to, and showed a blank sheet while the page rendered.
Document::without_marks now removes the marks of a kind from every page
once per dialog (UnmarkedPages), each page shown renders from it off the
UI thread, and the page's thumbnail stays until it arrives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scan & OCR ▸ Enhance scanned file (Acrobat's Enhance ▸ Scanned document)
now cleans up scanned pages: background removal (the camera clean-up's
level 0-100), edge shadow removal (book gutters, copier borders) and
despeckle, each its own switch.
The camera tool renders the page and replaces it with a picture, which
would drop a scan's invisible OCR text, comments and links. Instead each
scan image (pictures covering at least half the page, each at least a
tenth) is decoded, cleaned and written back into its own image object at
the same size and colour space: a JPEG stays a JPEG (quality 90), other
grey/RGB images are stored losslessly, a 1-bit image stays 1-bit with its
decode array, and every other dictionary entry (SMask, Interpolate, ...)
is kept. An image several pages share is cleaned once. What can't be
cleaned (CMYK, indexed, JPEG 2000, JBIG2, CCITT, colour-key masks, broken
data, oversized images) is reported, never skipped silently; pages that
are no scan are listed too.
The engine works as a job like Optimize (scan_clean_job, run on a worker
with progress and cancel, apply_scan_clean refuses a result for an older
document state) and applies one undoable step, "Enhance scanned file".
pdfcraft-optimize exposes decode_samples/encode_samples for in-place
pixel edits. camera_clean gains clean_samples for grey and RGB samples
with separate switches and an edge-shadow pass; the camera tool's output
is unchanged. page_enhance_scan drives it headlessly.
Not yet an option of Recognize text. Refs #4119.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both sides added modules to crates/redact/src/lib.rs (user_regex here, type3 upstream); keep all of them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts:
# crates/automation/src/lib.rs
# crates/engine/src/lib.rs
# crates/ui-egui/src/lib.rs
# crates/ui-egui/src/panels.rs
Main's module layout (#4889) moved what this branch changed: the tools to
automation's dispatch.rs (the levels parser to json.rs), the edit to
engine's edit.rs, edit/labels.rs and edit/run.rs, Document::text_styles
to document.rs, the dialog state to state.rs and app.rs, and the menu
item to panels/right.rs and panels/bookmarks.rs. The dialog module joins
the others in dialogs/. Main's craft-menu bar (#4752) lists its commands
by hand, so bookmark.from_text_styles is added after
bookmark.from_structure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
main deleted crates/engine/src/reexports.rs and moved its re-exports
into crates/engine/src/lib.rs; the comb_auto_text_size re-export this
branch added now sits in lib.rs. forms_ui.rs moved to
crates/ui-egui/src/tools/; the branch's hunks carried over unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Main moved to egui 0.37 (#4591) and took the menus change (#4752).
Conflicts:
- edit_text_ui.rs: the paragraph box keeps the shared input id, now
Id::unique; the added-text box's shared id and the unit tests' ids
follow.
- content_ui.rs: the added-text box keeps its per-paragraph layouter
(which places each line itself, so main's .align(halign) is left out)
and takes main's inset, margin(4, 2), and its "Added text"
accessibility label.