- 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>
# 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.
The doc comment for SplitPart had drifted above the LabelStyle re-export; it is back on SplitPart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a static XFA form gives its fields the values in its datasets
(a form filled in by another application). Each value went through
forms::set_value, which reads the whole field tree to find the field,
writes it, and recalculates, reading the tree again. With n values that
is about 2n readings of n fields: quadratic. A public 136-page form with
about 6,000 fields took 261 s to open, a 95-page one with about 4,000
took 112 s; 8,000 synthetic fields took over ten minutes.
forms::set_values fills a batch as set_value fills each in turn, reading
the form once and recalculating once after the last value (the
calculation order, ISO 32000-2 Table 224 /CO, runs once over the final
values). Fields with the same fully qualified name (12.7.4.2) still share
the value. A name the batch already wrote, or a twin of it, makes the
form be read again first, so each write starts from what the earlier one
left, as one by one. Each value reports its own result; a failed
recalculation fails the batch, as it fails set_value. The engine collects
the changed values and fills them with set_values, finding fields by name
through a map rather than a scan.
The two forms now open in 4 s and 3 s, with the same fields, values,
warnings and document information as before; 8,000 synthetic fields open
in 1.5 s.
Tests: set_values matches set_value one by one (values, twins, the
calculated total, every appearance, saved; refusals for an unknown name,
a read-only field, an unknown option and clearing a no-toggle-off radio
group the batch selected); it reads the form a fixed number of times; the
engine takes 59 data values on a 60-field static form exactly as filling
them one by one does (the read-only one refused); and opening 800 fields
takes under 16 times as long as 100 (about 4x now, about 48x before).
Claude-Session: https://claude.ai/code/session_01CBVyQEwtmEw3mS1MoAiwrK
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Five XFA tests failed at random on a busy machine. The scripts of an
opening or a change share a 3 s wall-clock budget (a click's share 5 s),
each script has 1 s or 3 s, and the engine has 10 s to start; under load
the tests went past one of them, and the scripts they check were skipped
or did not start.
Session::with_xfa_time_scale (and Automation::with_xfa_time_scale)
multiplies each of these limits for a test; the default stays 1. The
script session counts the engine threads it starts on the calling thread
(xfa_session::engines_started).
The hundreds-of-calculations tests run with the limits raised, and the
engine's test now checks that opening starts one engine and a change one
more. A new test sets the factor to 0 and checks that the budget skips
all 210 calculations and says so. Three more tests that failed the same
way under load run with the limits raised.
Main took integration batch 6 (#4566). Conflict in edit_text_ui.rs: the
overlay keeps this PR's view_id with main's interact_pos comment. The
two TextDraft literals from #4441's tests take selection and
typing_from.
CodeRabbit on #4560:
- The bookmark titles are the document's text, so the edit now needs
copying permission as well as assembly, and Document::text_styles (the
dialog's samples) is empty when copying is withheld; the automation
text_styles tool already checked it.
- A heading went on to the next row whenever it was close below in the
same style, so same-style headings in two columns on staggered rows ran
together into one bookmark. A line continues a heading only when it lies
under it.
- Lines found their style by a scan over every style so far; they are now
indexed by font, weight, slant and quarter-point size, so a page with
thousands of styles no longer makes the scan quadratic.
Regression tests for the first two (each fails without its fix).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LymhVaGZCWwB9HNEbLzeMM