Commit Graph
332 Commits
Author SHA1 Message Date
FredAppsandCodex GPT-6.1 75e012de9a fix(edit): compose inside-scope group translations in order
Fold a leading transform inside the image scope as cm followed by the local transform; an enclosing transform uses the opposite multiplication order. This preserves the complete requested image and crop displacement under an outer scale. The exact crop-arrange-group-move fixture previously moved only five points for a ten-point request; saved geometry and rasters now match an independent direct Transform control across both axes and signed offsets. Full scoped Linux native, wasm and repository gates pass.

Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
2026-11-01 17:41:13 +01:00
FredAppsandCodex GPT-6.1 e677c009b3 fix(edit): restore inherited text state when arranging Forms
Track the text-state operators with saved drawing state, restoring inherited spacing, scaling, leading, rendering mode, rise and known font before relocated Form artwork. Emit restoration inside BT/ET, and leave a font selected by a replayed ExtGState authoritative over an older direct Tf. The exact inherited 12 Tc fixture failed its saved-PDF raster comparison before this fix; zero-spacing and destination-state controls now preserve rendering. Scoped Linux native, wasm, clippy and formatting checks pass.

Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
2026-11-01 17:41:13 +01:00
FredAppsandCodex GPT-6.1 8669aa48aa fix(edit): preserve graphics state chronology and mask identity
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>
2026-11-01 17:41:12 +01:00
FredAppsandCodex GPT-6.1 9a1334e04d fix(edit): preserve image scopes and content ownership
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>
2026-11-01 14:47:46 +01:00
FredAppsandCodex GPT-6.1 3a0ea63c33 fix(edit): share saved drawing operands across graphics scopes
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>
2026-11-01 14:47:46 +01:00
FredAppsandCodex GPT-6.1 43ad4bf418 feat(edit): expose image crop ownership to frontends
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>
2026-11-01 14:47:46 +01:00
FredAppsandCodex GPT-6.1 f62c1cfafe refactor(text): share source font names and selection defaults
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>
2026-11-01 12:54:52 +01:00
FredAppsandCodex GPT-6.1 f607d26d14 fix(edit): preserve printing state when arranging images
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>
2026-11-01 12:41:19 +01:00
FredAppsandCodex GPT-6.1 3c394bb860 fix(edit): remove only editor-owned image crops
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>
2026-11-01 12:37:24 +01:00
FredAppsandCodex GPT-6.1 1a2f7b2a34 fix(edit): refuse arrange across crossed scope boundaries
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>
2026-11-01 12:33:51 +01:00
FredAppsandClaude Opus 5.5 eb0d5e64c7 refactor(edit): fold duplicated image scope and arrange helpers
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>
2026-11-01 12:33:50 +01:00
FredAppsandClaude Opus 5.5 71b39aec5b refactor(edit): drop test-only image API and dead graphics-state code
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>
2026-11-01 12:33:50 +01:00
Combiler01 fdc0e6404a fix: address review feedback on arrange, image flips, rotate knob, and format panel
- [P1] Arrange: track and replay inherited non-ExtGState drawing state (colors, line widths, etc.) across q/Q scopes for stencil and form artwork
- [P2] Flips: route ImageEdit::Flip through coordinate-aware transform logic to swap axes when rotated at odd quarter turns (90°/270°)
- [P2] Rotation knob: measure clockwise angle delta from drag start relative to the rotated image center to prevent reapplying existing angle
- [P2] Format panel: connect superscript/subscript controls to line editor and content draft selections, and update parity evidence
2026-10-31 23:18:58 +05:30
Combiler01 d2d28c7728 test(edit): restore regression test rewritten_paragraph_is_drawn_after_backgrounds_between_its_rows (#2377) 2026-10-31 19:40:44 +05:30
Combiler01 e781d31187 fix(ui, test): resolve font-size inspect query and command-key paste tracking 2026-10-31 17:09:50 +05:30
Combiler01 79eed72c08 Merge upstream/main into main preserving custom features
- 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
2026-10-31 15:50:31 +05:30
Combiler01 87c1e858e3 Coderavbit Fixes 2026-10-31 14:23:19 +05:30
FredApps d7f3e67141 fix: bound backdrop edits and withhold unsafe integration behavior 2026-10-30 14:45:50 +01:00
FredApps 673178b70c fix(edit): require intact streams when identifying page backdrops 2026-10-30 13:21:04 +01:00
FredApps 25f3d12960 style: format checked soft-wrap budget assignment 2026-10-30 12:54:56 +01:00
FredApps ee71348bc8 fix: bound soft-wrap metadata and preserve unknown backdrop content 2026-10-30 12:47:37 +01:00
FredApps bdf56209ce style: format combined paragraph regression 2026-10-30 11:08:55 +01:00
FredApps 7d3d4830e5 Integrate remaining six PdfCraft parity PRs with conflict resolutions
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.
2026-10-30 11:02:07 +01:00
FredApps b21af3b969 Integrate 21 clean-merging PdfCraft parity PRs
Preserve each original PR head and its authorship as a merge parent.
Includes #4304 #4335 #4384 #4404 #4179 #5300 #4719 #4541 #2815 #4620 #3789 #3779 #2960 #5208 #4596 #3636 #4494 #4013 #5134 #2133 #2635.
Conflicting candidates skipped. Combined CI pending; see docs/integration-20261030.md.
2026-10-30 10:35:42 +01:00
Dylan M. Taylor 3517d05b47 Update prompt shows the app icon and accent color instead of a letter (#5388)
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#b70228, its red, from the icon (#5245)). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).

craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
Clippy fixes for Rust 1.99 in untouched code (same logic):
- crates/edit/src/images.rs
- crates/forms/src/appearance.rs
2026-10-30 01:27:58 -04:00
RiceDee 5a86f35210 fix(edit, ui): alignments follow the right rows; Format by keyboard keeps the box
Four holes CodeRabbit found on 648dd428:

- alignment_frame framed a one-row paragraph when any alignment entry
  was set, including one past the draft's rows, and then moved rows
  with no alignment of their own to the column's edge. It now takes the
  draft and frames only when every draft row is aligned, by its own
  alignment or the paragraph's.
- remap could not tell which of two same paragraphs an edit took
  ("A\nA" to "A"), and kept the deleted one's alignment. remap_at takes
  the caret where the edit ended, and the typing path (follow_typing,
  both editors) passes it from the box's state. The caret only chooses
  between readings that fit both texts.
- Both editors judged a focus loss by where a click landed, so focusing
  a Format text control by keyboard or a screen reader (no pointer)
  closed the box before the alignment was chosen. They now also judge
  by where the keyboard went (focus_pos): focus outside the document
  view, or on the added-text box's own buttons, keeps the box open as a
  click there does.

Each change has a test seen failing before it.
2026-10-29 21:30:21 +11:00
ihsan mokhlisse 55a933800b M7: keep a typed space run through a saved width change (#2517)
The wrap's space count has to survive an incremental save and a full rewrite, and a width-only text_edit has to reflow with that count. A recorded editor session narrows the box and widens it again.
2026-10-29 10:42:20 +01:00
ihsan mokhlisse bb7186d87c M7: restore the exact gap a wrap consumed, not a guessed space (#2517)
wrap() already dropped the whole gap at a wrap point (#4199); rejoining it always guessed a single space back, so two or more typed spaces silently became one, and a Chinese/Japanese word split (which has no space of its own) could gain a spurious one. wrap() now records how many separator characters it consumed at each wrap (0 for a CJK split); the /PCSoft marker carries it as /N (a file saved before this, or by another writer, reads as 1); reflow_text and the mixed-style row editor insert exactly that many spaces back instead of assuming one.
2026-10-29 10:13:01 +01:00
ihsan mokhlisse dbeb9fa963 M7: move a paragraph instead of rewriting it to trigger a stale-number regroup
wrap()'s own test for mixed exotic whitespace fed through it directly, bypassing the caller's CR/CRLF normalization, needed its expected value updated for #4199's keep-every-space design, landed on main after this branch's base.
2026-10-29 10:11:19 +01:00
ihsan mokhlisse b282ec26be M7: check a shared text object through the tools (#2517)
Editing either end of one text object, then saving and editing again, has to leave the other paragraph in place. The library tests did not drive text_edit and doc_save.
2026-10-29 10:11:19 +01:00
ihsan mokhlisse 59513aa887 M7: keep a preceding paragraph out of a shared text object's marker (#2517)
A partial rewrite of the last paragraph opened the marker at the shared BT, so the paragraph before it was inside the region.
2026-10-29 10:11:19 +01:00
ihsan mokhlisse 62af4f87ca M7: compile the paragraph fixture on current main (#2517)
The paragraph-key helper has to carry reflow text, which a width change uses. ClosePath on an empty subpath is a match guard so the edit crate still passes clippy.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 21b911ae42 M7: close a shared text object before the next paragraph (#2517)
A partial rewrite put the paragraph marker around the shared ET, so the neighbour in that text object joined the paragraph and the next edit deleted it.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse cb832b0206 M7: close a partial rewrite before the next marked region (#2517)
An unmarked paragraph was closing its marker at the following line's BT, which is after that line's BDC. The close now follows this paragraph's last ET.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 2bc2933988 M7: rejoin a wrapped line when the box is widened (#2517)
A newline that was typed stays. A wrap is marked so a later width change can reflow it, and a short unmarked line stays broken.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse a84ae042d1 M7: close a partial rewrite's marker after the paragraph (#2517)
A row left in the source stays in the last paragraph. An extra marker is not placed across two marked-content regions.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 4ebb575a88 M7: keep a repeated step without TL, and a partial rewrite's neighbour (#2517)
Three or more rows at one repeated step stay one paragraph when the file has no TL, which is how LibreOffice writes each line. A rewrite that leaves a source row still marks the whole paragraph, so the neighbour survives a second edit. Dragging the box width keeps the line breaks.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 53e6d832af M7: keep an explicit leading, and a grown paragraph's neighbour (#2517)
The join limit follows the text object's TL when it has one, checked against the measured baseline gap. A rewrite of every row is marked so a new row one leading above the next paragraph does not absorb it.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse e228e0edfc M7: keep a blank line as a paragraph break (#2517)
A gap of 2.4 times the font size was still under the 2.5 times join limit, so two paragraphs became one and editing the first deleted the second. Once a paragraph has two rows, a wider gap than its own leading stays the next paragraph.
2026-10-29 10:09:18 +01:00
RiceDee 648dd42810 fix(edit): a column is measured by its whole rows; an isolated row keeps its width
Two holes in alignment_frame:

- A column's right edge came from the runs that start at its left
  edge, so a row of several styles ended at its first run, and the
  column could fail to hold a title wider than that run. It now takes
  the right edge of each complete row (row_rect).
- A row with no column to align in was framed by its own box, which
  the rewrite also used as the width its text wraps at: changing an
  isolated row's text together with an alignment wrapped it at its old
  width, word by word. When the text changes and no column holds the
  row, alignment_frame now returns None, so the row keeps the width it
  has without the alignment. With the text unchanged the own box is
  still the frame, so aligning twice is still a no-op.
2026-10-29 19:12:11 +11:00
RiceDee b9940d7d81 fix(edit): a lone row aligns in its own column, not a closer page-wide heading
alignment_frame measured a one-row paragraph's alignment in the frame
of the nearest overlapping block. A full-width heading closer to the
title than the title's own body column gave it a page-wide frame, so
centring or right-aligning the title moved it out of its column. It now
considers every overlapping block whose column holds the row and takes
the narrowest, the nearest on a tie, so a page-wide heading gives the
title no frame while a narrower column that holds it does. When no
column holds the row it is still measured in its own box.

The comment about page-wide headers named an exclusion the filter
never made; the doc comment now says what is chosen and why.
2026-10-29 18:38:05 +11:00
FredApps d9ddd8a8b0 Integrate KenM76 PR #4906 2026-10-29 02:35:58 +01:00
alehel 9aaac56417 Merge remote-tracking branch 'refs/remotes/origin/main' into fix/background-hidden-by-page-backdrop
# Conflicts:
#	crates/engine/src/tests.rs
#	crates/ui-egui/src/dialogs/marks_ui.rs
#	parity/acrobat-features.toml
2026-10-28 23:18:52 +01:00
aiongg 750012c75f Merge pull request #2402 from wstock/fix/2368-flatten-no-rotate
Issue #2368: flattening keeps a NoRotate widget upright on a rotated page
2026-10-28 17:36:43 -04:00
RiceDee 43f848d532 Merge main into feat/align-selected-lines
Main split and refactored the text-rewrite code this PR changes
(automation dispatch.rs, edit added/ and text/, text_regions/styled.rs).
Conflicts in added/store.rs, text/rewrite.rs and text_regions/styled.rs
take main's structure, with per-row alignment applied in it:

- rewrite.rs: LineWriter keeps the paragraph's alignment and the rows'
  own (align, aligns); each line looks up its row's alignment
  (align_of) for justify and for its offset. Alignment::offset takes the
  row's alignment. Wrapped lines carry their row, as wrap returns it.
- styled.rs: wrap_styled_rows also returns each row's draft line
  (row_lines), which the kept-rows check counts aligns by.
- Imports for the moved modules (alignment_frame, BlockStyle,
  align_code, align_from, content in dispatch.rs).
2026-10-28 19:34:21 +11:00
Dylan M. Taylor de73c35e17 Refactor edit for strict Clippy checks 2026-10-27 23:12:51 +00:00
Combiler01 afbc1ed1b7 fix: address CodeRabbit review recommendations across edit, engine, and ui 2026-10-27 15:30:53 +05:30
RiceDee 7679f6b556 fix(edit): keep column frames local and clear stale history selections 2026-10-27 19:02:30 +11:00
RiceDee 6733d9eaee Merge main into feat/align-selected-lines 2026-10-27 17:09:44 +11:00
Combiler01 2650cbc906 Merge branch 'upstream/main' into main with layout adaptation 2026-10-27 09:55:01 +05:30