Replay repeated ExtGState names in last-application order. Compare each complete soft-mask value and its establishment matrix instead of only its group reference, and retain cached shared definitions across saved states. Saved-PDF raster regressions cover Alpha versus Luminosity over the same group, repeated aliases at different matrices, and ordinary sequence/single-mask controls. Definition checks also cover backdrop and transfer parameters. Actual baseline and reverted-comparator tests fail; scoped Linux native, wasm, clippy and formatting checks pass.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Project the visible user-space bounds centre for the drag preview, matching the RotateBy engine operation. The full image centre differs for cropped artwork and caused the preview to jump on release.
The regression compares actual painted preview corners with saved/reopened image corners and verifies a requested 90-degree turn on cropped and uncropped controls, including page rotation. Linux scoped clippy, 106 tests, wasm, formatting, layering and assets pass.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Invert the actual affine basis instead of projecting onto assumed orthogonal axes. Capture the displayed crop frame for handles and apply its relative transform to the full artwork, preserving author clips on rotated pages. Exact out-and-back gestures preserve bytes and undo state, including off-centre presses.
Synthetic headless regressions verify sheared and rotated images, crop-frame anchors, press-offset independence and saved/reopened placement. Degenerate and nonfinite bases are refused; the inverse guard has a negative-control test.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Only request a script conversion from the panel; the caller decides whether conversion changed the text. Do not create a paragraph rewrite merely because the button was clicked. A hidden QQQ fixture checks both script buttons preserve document bytes, undo history and saved rendering, while supported conversions remain covered. Actual reverted-fix raster failure and scoped Linux native, wasm, clippy and repository checks passed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Measure Mixed with the active body font and include the input margins and caret allowance. Keep that minimum stable while typing so the full label is visible without shifting the row on each keystroke.
A themed panel regression fails against the old 40-pixel input, verifies the text fits inside the field margins and keeps the alignment control inside a 272-pixel pane. Linux scoped clippy, format tests, wasm, formatting, layering and assets pass.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
The pinned integration consumes native clipboard shortcut keys and delivers PasteImage pixels. Handle those payloads in order, keep text cache invalidation, and use the delivered image even if the OS clipboard changes before processing. Preserve alpha and validate dimensions and byte counts before allocation.
Remove release timestamps so Windows clipboard history cannot reuse a completed paste gesture. Keep explicit shortcut fallback for control-channel and browser internal-image paste, with modal, focus and permission guards unchanged. Tests cover both release orderings, history after prior paste, mixed batches and duplicate prevention.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Include the mixed-size flag in the font-size draft cache key. Selecting a mixed block can preserve the previous representative number, so a value-only key left the field showing 24 instead of Mixed. Unchanged formatting still preserves partially typed input across frames.
The headless regression covers uniform-to-mixed and mixed-to-uniform transitions with the same number, draft preservation and a single committed edit. Linux scoped clippy, tests, wasm, formatting, layering and assets pass; reverting the cache fix reproduces the stale display.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Document the existing policy: mixed plain and target-script text normalizes to that script, then to plain text, rather than reconstructing the original mixture. Undo restores exact prior text. Cover both scripts in engine and headless tool tests, including document-byte restoration, redo and save/reopen, and record the evidence in parity. The new tests pass against unchanged conversion logic; this is policy documentation and coverage, not a product behavior fix. Scoped Linux native, wasm, clippy, formatting, layers, assets and parity validation passed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Map horizontal and vertical tool actions to PDF user-space axes after accounting for page rotation. The tool description states the displayed-coordinate semantics. A four-quadrant raster and transformed-corner regression covers both actions at 0, 90, 180 and 270 degrees, with undo between actions; it fails on the wrong axes before this fix. Scoped automation clippy, all 354 tests, wasm, formatting, layers and assets pass on GGEO-AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Record image crop and stream ownership, visible orientation, clipboard and formatting fixes, Home controls, and reloadable font discovery as PR candidates. Estimates remain unchanged.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Offer Remove Crop only for editor-owned clips, reset draft cropping to the author-visible bounds, and remove only an owned crop when confirming the full image. Active crop mode owns presses on its page so underlying text and marquee selection cannot steal crop controls. Label the painted controls for accessibility and automation.
The author-clip regression opens the real menu, resets a draft crop and confirms it while preserving the authored clip. It failed before ownership checks and again before the router fix.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Use matrix resizing whenever image axes are turned or reflected on screen, including document and view rotation. Share the visible grip geometry for cursor claims and hit routing, and use only the drawn rotate knob. Map flip labels to the visible axis and defer Cut selection clearing to its result.
Regressions cover all page/view rotation combinations and 64 outward drags across every grip, asserting fixed anchors and pointer tracking for upright and rotated images.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Reject finite but singular or overflowing determinants at the engine boundary. Promote added images for arbitrary transforms, as RotateBy already does, while preserving editable quarter turns and reflections. Regressions verify transformed placement, promotion, undo and refusal without mutation.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Omit pinned tools from Recommended and keep unpin buttons in the accessibility tree without requiring pointer hover.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
An explicit size commit normalizes mixed-size selections even when the value matches the displayed size. Caret-only script commands format the whole draft. Mathematical minus uses reversible Unicode forms and ASCII hyphen-minus remains unchanged. Correct the applied-selection comment and document the character policy in parity and the tool.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Do not reuse stale native images for unsupported clipboard contents, while preserving web internal image paste. Observe text paste before focus gating and keep image-less key releases silent. Tie release fallback to V gestures. Check modification permission before copying and clear selection only after successful deletion.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
An original image and an added item may share an XObject without sharing editable metadata. Require the selected drawing stream to own the added item instead of falling back to any matching resource. The regression actually moved the added sibling before the fix; scoped native and wasm gates now pass.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Move editor crop scopes with grouped images while retaining fixed author clips and folding repeated transforms. Arrange keeps relocated original artwork in its own stream beside an added item, so later edits or deletion cannot discard it; unusual shared owned streams are refused. Refuse nested marked-content extraction when its tags cannot be preserved. Synthetic regressions and the headless group-move, undo/redo, save/reopen, arrange, delete and render lifecycle fail before these fixes and pass afterward.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Keep dash, color and extended printing operand payloads shared across q snapshots. Copy-on-write color updates preserve saved values without multiplying large operand arrays by nesting depth. A pointer-sharing regression failed before the change; scoped native tests, clippy and wasm check pass on GGEO-AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Report active PdfCraft crop markers through PageImage::has_editor_crop and page_images.has_editor_crop so frontends can distinguish removable editor crops from author clipping paths.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Resolve explicitly configured font roots while skipping descendant links; retain all accepted fonts within the existing bounded scan. Allow an explicit Reload without duplicate workers or an app restart. Three actual reverted-fix regressions fail; scoped engine/UI clippy, 2400 tests, wasm, formatting, layers and assets pass on AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Reconstruct moved edges from the fixed opposite anchor in f64 and round each coordinate once. This avoids cancellation introduced by adding a nearly full-width UV displacement to the old screen edge. The deterministic Linux CI case fails against the previous implementation and passes with the fix.
Check scale on both axes in screen space against a derived f32 coordinate-rounding bound, using the stored rectangle dimensions. Verify fixed edges for every handle and reject a deliberately stretched result. Both CI regression seeds and 10000 randomized cases pass; scoped UI clippy/tests, wasm, formatting, layering and assets are green on AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Describe editor-owned crop removal, preserve authored clipping paths, and link the scope/state regressions in parity evidence. Record the maintainer changes without changing readiness estimates.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Use the common image-selection constructor across owners, remove obsolete crop and copied-coordinate fields, keep only a shortcut timestamp, and make the internal UI modules private. Combined GGEO-AI02 validation: 4198 tests passed, scoped clippy, doctests, fmt, layers, assets, parity and wasm; 25-step CLI save/reopen smoke and real-shell screenshot checked.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Fill five notification/search entries in all catalogs and the missing Home tool labels in ckb, lo, nb and th. Correct Albanian crop labels and Italian/Kazakh unpin meanings. Catalog additions are AI-assisted and not separately native-reviewed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Contain discovery panics and publish the empty result so the font menu leaves its pending state. A synthetic panic regression fails when containment is removed.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Menu paste refuses cached images when system text supersedes them. Keyboard and menu paste share the text check; image validation reuses Create. Expire shortcut context by elapsed time even when frames are sparse. Regression tests fail with the old checks restored.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
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>
Remove the rectangle helper whose only caller was the deleted crop test, and format the preserved text_script tool additions.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Use the shared content Matrix, one image-frame painter, texture-mesh builder, crop-pill style and rectangle comparison. Replace rotated resize arms with signed anchors and reduce crop arithmetic while preserving UV and screen scale. Reuse the edit-generation image cache for drag painting and image menus, and keep the menu flag temporary.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Refuse relocation into transfer, overprint, black-generation, undercolor-removal or halftone state that cannot be restored. Preserve implicit device color spaces before sc/scn and restore rendering intent from both ri and ExtGState RI, including its initial default. Three synthetic regressions fail against the previous implementation; all 411 edit tests and scoped clippy pass on GGEO-AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Keep invalid image geometry and drag coordinates out of crop clamps and discard an invalid in-progress gesture. The regression panics at f32::clamp without the guard (cropred) and passes with it (cropgreen2), alongside 81 image and crop checks.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Share click handling, set crop mode in the image menu, remove duplicate export and inactive editor entries, and format shortcuts for the platform. Add a selection constructor for callers.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
Identify crop paths with a marked-content point and preserve ownership through arrange reconstruction. Find the same isolated or outermost image scope as Crop, preserving authored and shared clips. Both regressions fail with the fix reverted; all 410 edit tests, scoped clippy, fmt, layers and assets pass on GGEO-AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
crop_rect was only ever reset to None outside tests, and crop_mode was
always set together with crop, so readers now test crop.is_some() and
the writers are gone. The fields stay until the last struct literals in
content_ui stop naming them. Tests that set crop_rect to drive the crop
now set the crop's box the same way crop_input did.
The rotated-drag match had a DragTarget::Rotate arm that the turning
branch above always pre-empts, and image_input's ImageAction parameter
was unused (let _ = &action).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HandleId duplicated ImageHandle with conversion tables both ways; the
image engine now takes ImageHandle, which also carries the cursor icon,
and the crop grips map through one GRIPS table instead of two index
matches. ImageObb loses from_center_size_angle, scale_proportional,
project_delta, new, handle_id, handles_with_ids and the width, height
and theta fields, which only the test of scale_proportional read.
reset_crop, RESIZE_ACCENT, image_owns_pointer and DragTarget::CropHandle
had no callers, and turn_degrees is test-only. The image engine is
private and its unused re-exports are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image panel crops through Edit::UpdateContent with the fractions it
reads, and nothing else called update_image_crop. Removed with its test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Format text's Superscript and Subscript had no headless path (AGENTS §3):
text_script toggles them on a paragraph, or a byte range of its editor text,
through the engine's text_script and writes the paragraph as text_edit does.
The parity entry now lists the tool and its tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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>
is_bulleted, is_numbered, toggle_bullet_list and toggle_numbered_list had no
caller outside their own tests: Format text shows bulleted and numbered lists
as planned. Removed with their tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Require both graphics and marked-content boundaries to balance inside source, destination and removed spans. Regression fails before the guard; all 408 edit tests, scoped clippy, fmt, layers and assets pass on GGEO-AI02.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
PR #2155 review item A5 (part 2), plus the unreachable Ok(()) exits in
arrange_image from B11. No behaviour change for well-formed content:
- image_exclusive_scope and image_transform_scope were one scan; they are
now image_scopes, and transform/crop take ImageScopes::transform_scope.
The shared scan resets an open path at Q, as the exclusive one did; the
two only differed on content that leaves a path open across Q.
- arrange_image and remove_image_crop took page and index and listed the
page's images again; change_image now passes the page, its streams and
the image, and neither is public any more.
- content_units' four copied scan loops are one sequence_end.
- DrawingState keeps its line-style, intent and flatness operators in a
table; the w/J/j/M/d guards in compute_inherited_drawing_ops tested ops
extract_scope_state never collects.
- clip_path_to_ops: ImageState never builds a quadratic segment and only
stores finite points, so the QuadTo arm and finite checks were dead; it
now reports a segment it can't write instead of dropping the clip.
- target_destination_unit and determine_arrange_spans are shorter; the
Ok(()) exits for lookups that cannot fail are now errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #2155 review item A5 (part 1). No behaviour change for callers:
- crop_image, and the arrange_image/crop_image/remove_image_crop/MaskIdentity
re-exports, existed only for tests; tests now go through change_image.
- PageImage.smask was read only by tests; page_images no longer tracks
ExtGState for it. The nested-mask test keeps its rendered-pixel check.
- MaskIdentity's Display, ExtractedScopeState.effective_cm, the
ExtendedGStateTracker overflow_depth (an error ends the scan, so it was
never read) and the EffectiveGState one-line getters were unused.
- merge_dict always gets the ExtGState name, so the hash fallback for an
unnamed inline soft mask was unreachable outside a test.
- cid_to_gid_map_odd_length_is_trimmed_to_even exercised no crate code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reject malformed matrices instead of dropping non-numeric coefficients and applying an unintended transform. The regression failed against the previous parser on GGEO-AI02; all 1721 scoped tests pass with the fix.
Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
A resize of a turned image sends ImageEdit::Transform, which had no
headless path (AGENTS 3). image_edit now takes action transform with
matrix: six numbers in top-left-origin points, like the tool's other
geometry, applied after the image's placement. The tool converts it to
user space through the page's view mapping, and refuses a matrix that is
not finite, is flat (a·d − b·c = 0) or moves the image out of range.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore main's text where the PR changed things it did not need to:
intra-doc links turned into plain code spans (engine js.rs and
camera_edges.rs, forms raster.rs, fonts pdf.rs, sign keys.rs and
pkcs12.rs, ui-egui motion.rs and web_print.rs), the #[allow(dead_code)]
on render's byte_capacity and ui-egui's unit_for_tag (both are used on
every native target, as on main), the loosened combine_grid timings, the
forms test case rename (scratch() already numbers its folders), and the
computed xref size in a_recovered_page_tree_survives_a_full_save (always
5 there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
discover charged each candidate's size against the 64 MiB budget before
reading it, so proprietary or broken files early in a fonts folder could
use the budget up and hide openly licensed fonts after them. Charge only
fonts that pass validation and are not duplicates. Reads are still
bounded: every file read counts against four times the budget.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- page_image_at: PageImage::contains already requires the point inside
visible_rect (rect clipped by the placement clip), so the extra bounds
test before it decided nothing.
- image_dimensions: the 256 MB input-length guard repeated the caller's;
the decoder's max_alloc limit still caps what decoding allocates. Its
test no longer allocates 256 MB to hit that guard.
- font_content_key hashed every font file twice; one 64-bit hash plus the
length is enough to tell duplicate files apart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>