* fix(engine): map URL text line rects with view_rect_to_user and filter non-finite coordinates
* test(engine): assert concrete rotated user rects and overflow guards (#6469)
* M2: `info` lists the repairs made while opening, as Document Properties does (misc.cli-inspect)
`pdfcraft-cli info` now prints a `repairs` array: the notes about damage
repaired while opening that the app shows in Document Properties and
doc_info reports. It opens the document as the app does, once (the session
the dynamic XFA summary already used), so notes the app adds after reading
the objects, such as a page tree rebuilt from the pages found by scanning,
are listed too. A file the app cannot open for editing falls back to what
reading its objects repaired; a sound file prints an empty array.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
* M2: `info` runs none of the document's scripts while inspecting it (misc.cli-inspect)
The session `info` opens to read the app's repair notes and the dynamic XFA
layout now has JavaScript turned off, so an XFA form's initialize and
calculate scripts, which the app runs on opening, do not run during
inspection of an untrusted file. Before this branch the session was opened
for dynamic XFA forms only, and their scripts ran there too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
* M2: `info` reads the standards and repairs from the session it opens, and a laid-out form keeps its repair notes (misc.cli-inspect)
`info` now takes the declared standards (`Document::standards`, as the
Standards panel reads them) and the repair notes from the session it
opened, and reads the file once more only when the app cannot open it.
When opening lays out a dynamic XFA form, or writes any other revision,
the notes about damage repaired while reading the file now stay with the
re-read document, so Document Properties and `info` both list them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
---------
Co-authored-by: mtlcl410 <chuanlin@niar.org.tw>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- A draft with the same words (a space typed after the last) changes
nothing and is no undo step. It used to redraw the whole phrase.
- Rewriting words alone is refused when it would move any other text on
the page, visible text too, not only other OCR words.
- A cover stops at the words either side that stay: the padding of a
large word could reach over the ink of a small one beside it.
- Spacing other than one space between words goes to the whole-phrase
rewrite, which keeps it, instead of being dropped.
- A squeeze is relative to the scale the words already have.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deleting one word from a recognised line covered the whole line and
redrew it in Helvetica, though nothing but that word had changed.
A text edit of an OCR phrase now covers and redraws only the words it
changes; the rest stay as the scan shows them, still invisible text. Each
changed stretch is rewritten as a phrase of its own, on a copy of the
document, and the edit falls back to the whole-phrase rewrite when words
can't be told apart or a kept word would move. A deleted word leaves its
gap. A replacement a little too wide is squeezed across to fit, by up to
a quarter; wider than that it takes over the next word. A change of
formatting, or a typed line break, still redraws the whole phrase.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Appearance export checks a stream's size in the encoding it is written in
(printable data as text, not as hexadecimal).
- Copy no longer writes a redaction mark's appearance, which import never
reads, so it does not use up the budget the other comments share.
- Rich text: a namespace declaration inside another attribute's value is not
a declaration, a '>' in a value does not end the start tag, and a prefixed
root keeps the default namespace its unprefixed descendants inherit.
- import_comments never applies the file's field values, whatever the
placement arguments (a non-finite offset used to fall through to them).
- The repair-log line for skipped appearances escapes control characters and
cuts each description to 200 characters.
- Tests for the shared budget through the export loop, a near-limit appearance
that round-trips, and the six-comment summary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
Refs #5989. Ctrl/Cmd+A selects every object on the page; copied objects paste as page content that draws the same, within a document or into another, with organize's bounded deep copy across documents. Headless: object_copy, object_paste, object_move all.
- import and import_comments work on a copy, kept only on success.
- XFDF export shares the 64 MiB appearance budget import reads over all
comments; import refuses a normal appearance of several states (XFDF has
no /AS to select one, ISO 32000-2 §12.5.5).
- FDF import nulls a page or page tree an appearance reaches (/Type resolved).
- Rich text: an inherited default namespace other than XFDF's is declared,
and a namespace declaration with spaces around `=` is recognised.
- A redaction mark's /Rect grows to hold its quads, so the outline shows.
- The repair log gets one line per import (first five, then a count), and
no longer claims the comment is drawn.
- Tests: the sixty-form chain, and the redaction outline is stroked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
- XFDF export: the finished appearance XML is checked against the import limit;
a page whose /Type is a reference stays out; stream data with a carriage
return goes out as hex (XML 1.0 §2.11 end-of-line handling); where /AS selects
one of several states, the selected stream is written.
- XFDF import: dash arrays of any length come back; consecutive <br/> keep the
blank line in /Contents; rich text written as text on a comment with no
/Contents comes back as /RC; namespace prefixes rich text inherits are declared
on it; appearances share one budget over the import (now tested through it).
- Redaction marks: the file's /AP and a /BE (none in ISO 32000-2 Table 195) are
dropped and the mark is drawn by PdfCraft; one with an area it cannot draw is
refused rather than placed invisible.
- FDF import: /P is not copied (the page is /Page, §12.7.8.3.4 Table 254); a
comment nested past 256 levels refuses the file instead of nulling part of its
appearance (a chain of 60 forms comes back whole).
- Engine: appearances an import left out go to the repair log for Import and
Paste comments.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
Edit text queued Prepare grouped text for every page that came into view, after a full
preparation on a copy of the document on the UI thread to see whether it could. Scrolling cost
that work per page, marked the document changed, added undo steps and inlined shared forms
although nothing was edited.
Now nothing happens while pages are shown. A click (press and release, no drag) on grouped
artwork, routed to the picture by the page router as before, asks once whether that artwork
shows text preparation would open (text::forms::grouped_text_at, which reads the page without
copying or changing it). If it does, the page is prepared as one undoable step and the
paragraph at the point clicked opens for typing. A logo or a group preparation would refuse is
selected as before, with no message and no change. A drag still moves the selected group, and
while a paragraph is open or an edit is waiting the click selects as before. text_prepare is
unchanged.
The docs and the parity entry no longer say text is prepared on entering Edit text.
The repair reads the page's own fonts. It ran only after a group was expanded, so the same
damaged map on text that was never grouped kept reading wrong. Preparation now repairs it
whenever it finds it, recorded in the repair log as before, and PrepareText (text_prepare)
succeeds when it repaired a map even with nothing to expand. A repaired map no longer matches
the damage's fingerprint, so preparing again changes nothing.