* 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 rewrite also takes away copies of its words. With a copy earlier on the
page, the words after it moved up the list, so the next change rewrote the
word after the one it meant: deleting John removed Smith and left John.
Before each change, the words up to the next one that stays must still be
where they were. If they aren't, the edit falls back to the whole-phrase
rewrite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backspacing over a word leaves the spaces either side of it, and the rule
that sent spacing to the whole-phrase rewrite took that for spacing someone
asked for, so the most ordinary deletion redrew the whole line again.
Spaces beside a change are nothing to draw: the words around it stay where
the scan has them, with the gap already between them. Only what has to be
drawn goes to the whole rewrite: spacing inside the new words, between two
words nothing else changed, or any whitespace other than spaces.
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.