Commit Graph
727 Commits
Author SHA1 Message Date
FredAppsandClaude Opus 5.5 f01823f6fa refactor(edit): one superscript/subscript toggle, in the engine
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>
2026-11-01 12:34:54 +01:00
FredAppsandClaude Opus 5.5 ac4f26a00d chore: revert unrelated churn from #2155
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>
2026-11-01 12:33:50 +01:00
FredAppsandClaude Opus 5.5 92de289ecc fix(engine): spend the font budget only on fonts the picker lists
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>
2026-11-01 12:33:50 +01:00
FredAppsandClaude Opus 5.5 3817a9f3ec refactor(engine): drop redundant image and font checks
- 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>
2026-11-01 12:33:49 +01:00
FredAppsandClaude Opus 5.5 4c70761803 refactor(engine): use pdfcraft_content::Matrix for added-image edits
run_edit_page_image carried its own inverse, bbox and multiply; Matrix
already has invert, bbox and then with the same formulas and the same
degenerate-determinant guard. Also drop the second finite check on Move
(checked on entry), match the added item with two finds instead of a
four-way chain, and toggle flips in one place for both the parallel and
perpendicular cases. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:33:49 +01:00
FredAppsandClaude Opus 5.5 29913dcfca Merge current main into #2155
Brings the branch up to date with main (47f42ba0), which moved 37 commits since
the merge base, including page-edge shadows (#2088), full rewrite after page
delete (#4279) and the licence-check stage (#2212). No conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 11:23:43 +01:00
FredApps 7165ff1ace Merge current main into PR4279 and retain unsigned delete-save policy 2026-10-31 22:09:56 +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 d67e720eed fix: address CodeRabbit review feedback on transforms, formatting, and UI 2026-10-31 19:20:44 +05:30
Combiler01 b512e3627f fix(engine): fold quarter-turns into AddedImage rotation and reject non-axis-aligned transforms 2026-10-31 18:44:20 +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
songzhihui 9447cdc810 M2: merge main and resolve CJK viewer integration conflicts 2026-10-31 01:14:02 +08:00
FredApps 7fe33d1cbd test: verify conservative review fixes across rendering and automation 2026-10-30 15:32:44 +01:00
FredApps 4791cacd2c test: qualify conservative tone retention and format review regressions 2026-10-30 14:55:44 +01:00
FredApps d7f3e67141 fix: bound backdrop edits and withhold unsafe integration behavior 2026-10-30 14:45:50 +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
Grzegorz MartensandClaude Opus 5.5 8bd156a4ac M11: Reduce File Size's descriptions say black-and-white images go to 300 ppi
doc_reduce, the engine's reduced_bytes doc, the optimize README's
presets and the optimize.reduce parity note still said every image
above 225 ppi goes to 150 ppi; black-and-white images go to 300 ppi
above 450 ppi, as CCITT Group 4 when smaller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 20:48:03 +01:00
Grzegorz MartensandClaude Opus 5.5 4543bae3bf M11: say redaction and export don't read Group 4 yet; Reduce File Size's descriptions
- Redaction doesn't decode CCITT, so a mark over part of an image the
  optimizer stored as Group 4 removes the whole image (nothing leaks),
  and Export ▸ Images refuses it. The optimize README and the
  mono-compression parity note say so. Later PRs of this stack add both
  (#4335 export, #4384 redaction).
- doc_reduce's description, Engine::reduced_bytes and docs/cli-batch.md
  now list the black-and-white step; docs/target-app-parity.md's
  optimize row gives the measured counts and no longer lists CCITT as
  missing; the M11 milestone row lists it as done.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
alehelandClaude Opus 5.5 acbc746a57 Merge origin/main into fix/delete-single-reply
No conflicts. main's changes to the files this branch touches (the page
cursor router in canvas.rs, hover tips in tools/comments, new tools and
document methods) don't meet this branch's hunks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 14:10:59 +01:00
Oliver HagmannandClaude Opus 5.5 67b5b2dcf6 A moved comment is drawn where it goes while it is dragged
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>
2026-10-29 05:50:58 +01:00
FredApps a66686cb0d Integrate KenM76 PR #4862 2026-10-29 02:34:57 +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 dc943eeb13 Merge pull request #4718 from alehel/fix/marks-update-preview-without-old-marks
fix(ui): Update previews the page without the marks it replaces
2026-10-28 17:59:41 -04:00
aiongg f824475202 Merge pull request #4631 from Lcoding15/fix/delete-one-radio-button
M6: Delete removes one selected radio button, not its whole group (#4609)
2026-10-28 17:55:17 -04:00
.CooCoo 222a124650 M5: Import comments takes another PDF and copies its comments onto the same pages (#4595)
* M5: Import comments takes another PDF and copies its comments onto the same pages

Import comments read only XFDF, FDF, XML, XDP, CSV and text data files.
It now also takes a PDF: each comment of that PDF is copied onto the
page with the same number, at the same place, with its appearance
stream, its pop-up, its replies and its layer. Comments on pages past
the document's last page are left out, together with replies to them.
Links, form fields and redaction marks are not copied. A comment named
like one already on its page replaces it, as an XFDF or FDF import
does. A PDF without comments is refused with "nothing in the file
matches this document", and a PDF that needs a password is refused.

- organize: import_comments copies the comments with the page copier
  that Insert Pages uses, and registers the layers they use.
- engine: Edit::ImportData reads a file that starts with %PDF- as a PDF.
  Any other file goes to the data importer first, and is read as a PDF
  when that fails, %PDF- occurs in its first 1,024 bytes and the PDF
  reader opens it.
- ui: Comments > Import comments lists .pdf files; form.import_data
  lists the same files as before.
- automation: the doc_import_data description names the PDF case.
- parity: comment.import-from-pdf is shipped.

* Import comments: an annotation that is no comment keeps its name

A comment imported from a PDF replaced any annotation of the same /NM
on its page that was not locked, a redaction mark or a widget, so a
link, screen or 3D annotation of that name was overwritten. Only an
annotation whose subtype is one of the copied comment subtypes is
replaced now; for any other, the copy is added beside it without the
name. The subtype is read through an indirect reference.

* Import comments: index each page's annotations instead of scanning them per comment

* Import comments: a layer that BaseState OFF hides stays hidden

* Import comments: a PDF with binary bytes before its header imports on an XFA form, and two comments with one name both land

On an XFA form, a decode failure no longer stops the import when a PDF
header is in the first 1,024 bytes; the PDF's comments are copied. Two
incoming comments on one page with the same /NM no longer replace the
same comment: the first replaces it, and the second is added without
the name.

* Import comments: an inline annotation on the page is replaced by name

An annotation written inline in a page's /Annots is made an indirect
object before names are matched, so an incoming comment with its /NM
replaces it instead of being added beside it.

* Import comments: two new comments with one name keep it once

When no comment on the page has the name, the first incoming comment
keeps its /NM and each later one with the same name is added without it.
2026-10-28 17:53:03 -04:00
aiongg 4d8dc3a1b7 Merge pull request #4560 from Lcoding15/feat/bookmarks-from-text-styles
M4: New bookmarks from text styles, for headings that aren't tagged (#1693)
2026-10-28 17:50:47 -04:00
.CooCoo 8f3a4f18bd M6: Organize pages ▸ Page properties sets the selected pages' tab order (#4516)
* M6: Organize pages ▸ Page properties sets the selected pages' tab order

Page properties in the Organize pages panel was listed as planned. In
the app, a page's tab order could only be set for every page at once,
with the form.tab_order commands, which offer no Unspecified. (The
form_tab_order tool already set chosen pages, Unspecified included.)

- Page properties (page.properties) opens a Tab Order dialog for the
  selected pages, with Use Row Order, Use Column Order, Use Document
  Structure and Unspecified. OK writes Edit::SetTabOrder for those pages
  as one undo step. The dialog opens on the pages' order, or with no
  choice made when they differ, and then OK is off until one is chosen.
  OK on the order the pages already have adds no undo step.
- Rows and columns run right to left in a document bound on the right,
  as Use Row Order and Use Column Order are described. PdfCraft reads a
  right-to-left binding from /ViewerPreferences /Direction /R2L
  (ISO 32000-1, 12.2, Table 150). Before, rank_tabs ranked them left to
  right.
- A page whose /Tabs is an indirect object now takes its row, column or
  structure order. Before, rank_tabs read only a direct name and ranked
  the page in annotation order.
- pdfcraft_forms::tab_orders and Document::tab_orders read each page's
  /Tabs. form_tab_order returns them as page_tabs, and reads without
  changing anything when it is given no order or field.
- set_option("dialog", "page-properties") opens the dialog.
- Parity: organize.page-tab-order is shipped, and its notes say the
  Actions tab of Page Properties (organize.page-actions) is still
  planned; the Organize pages grid notes list Page properties.

* Page properties: OK applies only to the document the dialog was opened on

The draft keeps the document's id. When another document is active by
OK, the dialog closes without changing either document.

* Menus: list Page properties… in the Pages menu

The menu bar now lists its commands explicitly, and every command with a menu group must appear in it.

* Page properties: apply the tab order only to an unchanged document

An edit, undo or redo made while the dialog is open can move the pages
at the stored indices. OK now closes the dialog without changing a page
when the document's edit generation differs from when it opened.

* Page properties: say when a document change cancels the tab order

OK shows a notice when the document changed or another document became
active while the dialog was open, so the user knows to open it again.
2026-10-28 17:48:29 -04:00
Eric Kaden 21f6c29d3e M6: form script messages keep their title, icon and buttons, and every one is shown (#4293, #4294) (#4306)
* M6: form script messages keep their title, icon and buttons, and every one is shown (#4293, #4294)

* M6: a form script message waits for a notice the toast already shows

* M6: form script messages past the queue's room are counted and reported

* M6: a sentence break in the merged parity note

* M6: an object that names no parameter is a message's text, as a String object or a caught error

* M6: the count of left-out script messages is said again if another notice replaces it before its time

* M6: a full stop between two sentences of the FormCalc parity note

* chore: re-run CI (a frame-budget timing test missed on a shared runner)
2026-10-28 17:46:11 -04:00
Adrian bcaead5404 M10: PDF ▸ Markdown export, through doc_export_office with a .md path (#1595) 2026-10-28 15:49:39 -04:00
alehelandClaude Opus 5.5 6a1783599d fix(comments): Delete asks the engine whether it would delete
The menus greyed Delete out from the listing's reply links while the
engine refused on the PDF's own /IRT and /Parent links, so the two could
disagree. pdfcraft_annot::deletable is now the one check: delete_annotation
runs it, and Document::annotation_deletable gives the Comments panel and
the page's context menu the same answer before they offer Delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:09:59 +01:00
alehelandClaude Opus 5.5 dcec1cecff fix(ui): Update's preview parses its unmarked copy once and keeps a page's failure to itself
The page shown was drawn through a fresh renderer each time, which parsed
the whole unmarked document on the UI thread; UnmarkedPages now parses it
once and keeps it. An error from rendering a page ahead (the pool retiring
its parser, a cancelled request) no longer marks that page failed: only
drawing it when shown decides, and a page that has a picture keeps it. On
the web, where the pool renders on the UI thread, nothing renders ahead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:00:34 +01:00
alehelandClaude Opus 5.5 28a31aebc0 fix(comments): previews lift a stamp whatever its replies' locks; Delete greys out over a locked reply
The locked-reply check in delete_annotation also reached stamp_preview
and signature_image, which only take the annotation off a copy to draw
the page beneath it: an unlocked stamp with a locked reply lost its drag
preview. They now use lift_annotation, which checks no locks.

The Comments panel's Delete (comment and reply menus) and the page's
context menu are greyed out while a reply below is locked
(comments::locked_below), rather than refusing after the click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:33:14 +01:00
alehelandClaude Opus 5.5 1e01497649 fix(ui): Update prepares the unmarked pages once, and shows the thumbnail while one renders
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>
2026-10-28 12:09:37 +01:00
alehelandClaude Opus 5.5 b3cbb77681 Merge origin/main into fix/marks-update-preview-without-old-marks
main split lib.rs: the unmarked_page field stays on PdfCraftApp in lib.rs,
its init and the render_pending check moved to app.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:50:04 +01: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
Claude 03fdf97cde Merge remote-tracking branch 'upstream/main' into feat/bookmarks-from-text-styles
# Conflicts:
#	crates/ui-egui/src/commands.rs
#	crates/ui-egui/src/dialogs.rs

Main split the command and dialog dispatch for its stricter clippy
rules; New bookmarks from text styles goes into the command list after
New bookmarks from structure, and its dialog into the dialog dispatch
next to the other dialogs with their own buttons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
2026-10-28 06:07:14 +00:00
Dylan M. Taylor 81c63c037a Group annotation, form and document operation parameters 2026-10-27 23:25:39 +00:00
Dylan M. Taylor 166f706e3e Refactor engine for strict Clippy checks 2026-10-27 23:12:51 +00:00
songzhihui 42aeec616d M2: resolve CJK viewer PR roadmap conflicts with main 2026-10-28 06:31:50 +08:00
Claude cace97e7b2 M4: one text style can't be given to two bookmark levels
A line goes to the first level whose style it is in, so the same style
for two levels, or one within the size tolerance of the other, left the
later level empty without a word. The edit now refuses it, for the dialog
and automation alike, naming the two levels (CodeRabbit on #4560).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbuSKAS2k5248LGFEapMUR
2026-10-27 21:02:46 +00:00
Claude 65d8385616 Merge remote-tracking branch 'upstream/main' into feat/bookmarks-from-text-styles
# 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
2026-10-27 20:46:00 +00:00
NetterSilverandClaude Opus 5.5 37bf4ee986 perf(engine): export page images on worker threads
Export > Image spent its time in the PNG, JPEG and TIFF encoders, one
page after another: on a 55-page manual at 150 dpi, drawing every page
took about 0.34 s and encoding it 1.1 s (PNG) to 2.0 s (JPEG).

Exporter::images exports a list of pages, drawing and encoding several
at once on worker threads, each with its own PageRenderer (as
automation's extract_parallel does), and hands the files to a sink in
page order on the calling thread. Workers draw at most one page ahead
of the sink and fewer run when 1 GiB can't hold that many of the
largest page. The first page or sink error stops the export with every
earlier page handed over, as before. A page panic becomes that page's
error, and a panicking sink stops the workers. One page, one core or
wasm keeps the per-page loop. Export > Image and the doc_export_images
tool use it.

Files are byte-identical. One core is left for the interface (at most
8 workers). With examples/export_bench, on a Ryzen with 16 threads
(shared), mean of two medians of 21 runs:
  PNG 150 dpi   1874 -> 340 ms  (-82%)
  JPEG 150 dpi  2106 -> 340 ms  (-84%)
  TIFF 150 dpi  2032 -> 375 ms  (-82%)
  PNG 300 dpi   3804 -> 590 ms  (-84%)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:41:38 +01:00
Combiler01 afbc1ed1b7 fix: address CodeRabbit review recommendations across edit, engine, and ui 2026-10-27 15:30:53 +05:30
RiceDee 6733d9eaee Merge main into feat/align-selected-lines 2026-10-27 17:09:44 +11:00
Claude f595a9677b Merge remote-tracking branch 'upstream/main' into fix/delete-one-radio-button
# Conflicts:
#	crates/engine/src/lib.rs
#	crates/forms/src/author.rs
#	crates/ui-egui/src/prepare.rs
#	parity/acrobat-features.toml
2026-10-27 06:03:23 +00:00
Combiler01 5cf09552ab Merge Conflicts #4889 2026-10-27 11:04:20 +05:30
Combiler01 2650cbc906 Merge branch 'upstream/main' into main with layout adaptation 2026-10-27 09:55:01 +05:30