* 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.
# 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
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
# 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
Two content conflicts, both from main splitting the files this branch edits.
crates/cos/src/document.rs: main moved Document into document/*.rs.
Keep that split. A missing /ID is still pdfcraft_crypt::file_id of the
encryption seed (document/open.rs); generated_id is gone. The regression
lives in document/tests.rs.
crates/engine/src/edit.rs: main moved EditCtx and run_edit into
edit/run.rs. Keep that split. Protect still draws its seed and default
owner password from getrandom on EditCtx and fails closed
(EditError::Protection) when the CSPRNG errors or returns all zeros.
Co-authored-by: jonathanwxh-cell <jonathanwxh-cell@users.noreply.github.com>