Commit Graph
1272 Commits
Author SHA1 Message Date
Hasan N. bf409db55c M3: pages shorter than the window sit in its middle (#5708) (#5759) 2026-11-09 08:38:40 +08:00
mtlcl410 6062cd6a44 M5: Replace Text keeps its replacement on the group's primary, the caret (comment.replace-text) (#5689) 2026-11-09 08:38:25 +08:00
mtlcl410 ee2398f710 M11: PDF/A verify checks that predefined XMP properties have their value types (optimize.pdfa-validate) (#6220) 2026-11-09 08:37:56 +08:00
3ae9093d61 M2: info lists the repairs made while opening, as Document Properties does (misc.cli-inspect) (#6518)
* 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>
2026-11-09 08:30:37 +08:00
42b21e5362 fix(render): a form XObject that draws itself is drawn once (hayro-interpret patch 89) (#6515)
* M2: a form XObject that draws itself is drawn once (hayro-interpret patch 89)

A form whose content names itself, directly or through another form, a
pattern or a glyph, drew itself again at every level until the page's
nested-paint budget (50,000) ran out. PDFBOX-5782's poppler-748 page 2,
whose form names a /Pages dictionary as /X0 and so finds itself in the
page's resources, took 38 s.

hayro-interpret now keeps the forms being drawn on the context and skips
a form already on it, reporting ContentTruncated (ISO 32000-2 §8.10.1: a
form is a self-contained description). The lenient fallback from a form's
own resources to the page's stays: MuPDF, Poppler and PDFium all draw a
name from the page's resources when the form's /Resources has no
XObject subdictionary (MuPDF and Poppler also when it has an empty one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* fix(render): a form that reaches itself through a pattern or a glyph is drawn once (hayro-interpret patch 89)

The forms being drawn are now kept per page and thread rather than per
context, so the tiling patterns, Type 3 glyphs, soft masks and groups
painted inside a form, which hayro interprets in contexts of their own,
see that the form is being drawn and skip it inside themselves. A form
drawn again after it has finished draws as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA

* chore: re-run CI after the Fill & Sign save comparison failed on the clock (#6066)

`fill_sign_enter_adds_lines_outside_click_commits_and_escape_cancels`
compares two saves whose /ModDate fell in different seconds; it passes
locally on this commit. No change.

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>
2026-11-09 08:30:14 +08:00
9b231f241d perf(render): undo TIFF predictor 2 a byte row at a time at 8 and 16 bits (#6505)
TIFF predictor 2 (ISO 32000-2 §7.4.4.4, Table 10) went through the
bit-level BitChunks/BitWriter path for every bit depth, re-reading the
previous output pixel through a new BitReader per pixel. Whole-byte
samples (8 and 16 bits per component) are now undone on byte slices,
each sample added to the same component of the pixel to its left,
wrapping at its width; 1-, 2- and 4-bit samples keep the bit-level path.
The output is byte-identical, including the dropped partial last row.

A page of 45 full-page 8-bit grey images so predicted (PDFBOX-1350)
renders in about 4 s instead of 23 s with the same pixels.

Vendored hayro-syntax patch 42.


Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

Co-authored-by: mtlcl410 <chuanlin@niar.org.tw>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:29:44 +08:00
b07a84f003 M5: a comment whose colour is written [r g b null] is drawn, as CMYK with the null read as 0 (view.render-annotation-appearances) (#6503)
* M5: a comment whose colour is written [r g b null] is drawn, as CMYK with the null read as 0 (view.render-annotation-appearances)

ISO 32000-2 Table 166 /C (and /IC, Tables 178, 180, 181; /MK /BC and /BG,
Table 192) is an array of 0, 1, 3 or 4 numbers whose length picks the
colour space. A four-entry array with a non-number in it, such as
[1 0 0 null] (PDFBOX-2699), made the /C parser give up, so a shape
without an appearance stream was not drawn at all. PDFium, MuPDF and
Poppler all read such an array as CMYK with the non-number as 0.

- annot: a four-entry /C or /IC reads non-numbers as 0; other lengths
  still need every entry to be a number.
- annot: the properties' fill uses the same parser (it read
  [r g b null] as RGB before), and /C no longer drops a non-number
  to make three numbers.
- forms: /MK /BC and /BG read a four-entry array the same way instead
  of dropping the non-number and drawing RGB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M5: comment properties report the stroke colour as drawn; a reference among four colour entries is not read as 0 (view.render-annotation-appearances)

props() reads a non-FreeText /C through the appearance parser, so a
[1 0 0 null] stroke reports CMYK cyan as drawn, and grey and CMYK
strokes report their converted colour. A reference among the four
entries of a /C array (ISO 32000-2 7.3.10) is left unreadable instead
of standing in as 0, so /C [1 0 0 6 0 R] is never drawn cyan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M5: re-run CI (fill_sign Escape test depends on the clock's second, passes locally)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M5: the comment list reports a comment's colour as drawn (view.render-annotation-appearances)

summaries() accepted only a three-entry /C, so a Square with /C [1 0 0 null],
drawn cyan and reported cyan by props(), had no colour in the comment list;
grey and CMYK colours were dropped the same way. Non-FreeText comments now
read /C through drawn_color (the array itself followed when it is given by
reference), keeping the three-number fallback and the FreeText /DA handling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M5: the render inspector lists a comment's colour as drawn (view.render-annotation-appearances)

The inspector accepted only a three-entry /C, with a referenced entry read
as 0, so a /C [1 0 0 null], a grey or a CMYK colour listed none, unlike the
comment list and the drawing. Non-FreeText comments now read /C by the
appearance builder's rule (one entry grey, four CMYK with a non-number as 0
and a reference unreadable, finite and clamped), with the three-number
fallback reading referenced entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

---------

Co-authored-by: mtlcl410 <chuanlin@niar.org.tw>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:29:31 +08:00
BFlat 8d5d39ef5e Combine files: a drop is one undo step, in order, and names what it can't take (#6645)
* Combine files: add a drop as one step, in order, and name what it can't take

Files dropped on the Combine files tab are handled as one batch: they join
the end of the list in the order dropped, as one undo step, from their path
or, when a drop carries only bytes, from those. A folder still adds the
files in it and its subfolders. Files of a kind Combine doesn't take are
left out and named in a notice, instead of joining the list as files that
can't be read. In a browser the dropped files are read one after another,
so they keep their order too.

* Combine files: let the web build use Incoming for dropped files

* Parity: Combine files takes drops in order, as one step, and names what it leaves out
2026-11-09 04:10:08 +08:00
BFlat 71aa9027d3 Merge pull request #6643 from storytold/merge/5404-export-paragraphs
Exports keep a wrapped paragraph and a bulleted list as paragraphs (#5404, merged with main)
2026-11-09 04:09:08 +08:00
BFlat 43f24aa169 Draw Thai in Fill & Sign, text boxes, form fields, headers, footers and watermarks (#6632)
Refs #2354. Thai typed into these tools is shaped and drawn from the Thai face through Add text's writer (now pdfcraft_fonts::shaped_text), searchable and copyable, instead of being written as ?????; a build without a Thai face refuses it with a message.
2026-11-09 02:56:34 +08:00
BFlat e0f90d5f2d Edit a PDF: select all, copy, cut and paste page objects within and between documents (#6639)
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.
2026-11-09 02:32:50 +08:00
BFlat c07e092ff5 Merge pull request #6637 from storytold/merge/3492-thai-line-breaks
M7: break Thai rows between dictionary words (#3492, merged with main)
2026-11-09 02:25:04 +08:00
bflatastic 70fd3ec969 Parity: Word export names the poem-only page among the joins it gets wrong
A page whose only text is short unpunctuated lines takes its longest
line for the right edge, so those lines join into one paragraph.
2026-11-09 01:18:57 +07:00
bflatastic 79e260f949 Merge #5404: exports keep a wrapped paragraph and a bulleted list as paragraphs
Resolved against main's ruled tables (#5154, #6626) and clipboard export:
the new tests pass main's rules argument to tables(), and the parity
notes keep both sides' wording.
2026-11-09 01:13:17 +07:00
bflatastic f6831d58ea Merge remote-tracking branch 'origin/main' into merge/3492-thai-line-breaks 2026-11-09 00:15:27 +07:00
bflatastic 8f5a5aeb89 Merge remote-tracking branch 'origin/main' into fix/thai-grouped-text-v2
# Conflicts:
#	docs/roadmap.md
2026-11-09 00:13:03 +07:00
BFlat da6ee1e62a Merge pull request #6634 from storytold/merge/1641-web-ocr
OCR in the web build (#1641, merged with main, two fixes)
2026-11-09 01:10:19 +08:00
BFlat c659211308 Merge pull request #6633 from storytold/merge/5454-ocr-phrase-size
An edited OCR phrase is drawn at one size (#5454, with the requested test)
2026-11-09 01:10:11 +08:00
bflatastic c8248e3b6d Merge #3492: M7: break Thai rows between dictionary words
Brings the Thai word-breaking branch up to date with main. The only conflict was the edit.add-text parity entry: main's evidence and notes (print inks) are kept, with the new Thai test added and the Thai wrapping sentence updated.
2026-11-08 23:58:18 +07:00
BFlat 485bc143a9 Merge pull request #6631 from storytold/merge/5498-word-export-language
Word and HTML exports name the document's language (#5498, merged with main)
2026-11-09 00:57:18 +08:00
bflatastic 8afe3f4f67 Merge remote-tracking branch 'origin/main' into fix/thai-grouped-text-v2
# Conflicts:
#	ROADMAP.md
2026-11-08 23:56:10 +07:00
bflatastic 2f492db9a3 Merge remote-tracking branch 'origin/main' into merge/5454-ocr-phrase-size 2026-11-08 23:39:29 +07:00
bflatastic 6bc6d80279 Merge remote-tracking branch 'origin/main' into merge/1641-web-ocr 2026-11-08 23:38:03 +07:00
bflatastic 1ea1c63f3d Merge remote-tracking branch 'origin/main' into merge-5498
# Conflicts:
#	parity/acrobat-features.toml
2026-11-08 23:33:35 +07:00
88024858c3 M10: Correct recognized text keeps the window's tabs and toolbar and shows the page (ocr.correct-recognized-text) (#6490)
* M10: Correct recognized text keeps the window's tabs and toolbar and shows the page (ocr.correct-recognized-text)

Scan & OCR > Correct recognized text drew the OCR Verify screen before
the tab strip and the mode bar and returned, so it took the whole
window: the tabs, menus and toolbar went away (on macOS the traffic
lights landed on the PAGES caption). Its source pane drew only a
document the screen had recognized itself, so opened on a document
(already searchable or not) it was blank until a run.

The screen now takes the document area under the tab strip and the
mode bar; the command palette shows over it too. Before a run, and
after a review is accepted or rejected, the source pane shows the
document in front at the page it is on. Another mode or the Home tab
puts the screen away, as Exit does; the command brings it back with
its review as it was. A run now shows the first page it reads (the
current page for that scope) instead of page 1.

Fixes #6485

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M10: OCR Verify keeps its review when the document is closed and shows a page it read (ocr.correct-recognized-text)

- Closing a document whose OCR review is still open (File > Close, the tab's
  Close, the snapshot bar's Close, Close all) brings the review forward with
  a notice to accept or reject it first, instead of closing the document under it.
- After a run the source pane shows a page that was read, over one skipped for
  its text; a run that read no pages keeps the page it was started on.
- PAGE shows the page's number over the document's page count.
- A cached preview of another document or page is dropped before rendering.
- The re-entry test checks a review and its correction survive another mode
  and Correct recognized text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M10: OCR Verify holds Quit for an open review, lets a failed Accept be rejected, and survives a worker that cannot start (ocr.correct-recognized-text)

- Quit and closing the window bring a document under OCR review forward
  with its review instead of exiting, as Close all does.
- Reject also throws away a reading whose Accept failed.
- A run or re-read whose worker thread cannot start leaves the screen
  usable and an existing review as it was.
- A page preview that fails to render is not rendered again on every
  repaint, only once the document changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M10: the web build's leave warning asks while an OCR review is open (ocr.correct-recognized-text)

A browser reload or a closed tab cannot be held the way Close and Quit are,
so the beforeunload flag the web page reads now also counts an OCR review
that is not yet accepted or rejected, by the same check that holds Close all
and Quit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M10: keep LF line endings in the files the leave-warning change touched (ocr.correct-recognized-text)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn

* M10: an OCR review holds a deferred close, a run on another document, and the embedding page's leave warning (ocr.correct-recognized-text)

A close that waited for a save checks again for an OCR review started
meanwhile: the reviewed document stays open, and Close all and Quit stop.
Run OCR on another document while a review is held is refused with a
notice, so the review keeps its corrections. The embedding page's
`host_dirty` hears an open review as work at risk, as the reload warning
does.

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>
2026-11-09 00:32:03 +08:00
BFlat edc5c757d3 Word tables get outer borders only where the page rules them (#6626)
A grid drawn with inside lines only no longer gets a box in Word.
2026-11-09 00:09:23 +08:00
bflatastic ae7a21d2b0 Edit grouped text with one click instead of preparing pages on sight
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.
2026-11-08 22:31:14 +07:00
bflatastic 4444355446 Merge grouped Thai text editing (#6249)
# Conflicts:
#	parity/acrobat-features.toml
2026-11-08 22:31:13 +07:00
mtlcl410 7d1dea0517 M7: Ungroup draws Form artwork in place, so its text can be edited (#3067)
Refs #2964, #3005
2026-11-08 22:14:30 +08:00
FredApps 5e0619d7eb Merge pull request #1977 from vangel76/feat/redact-mark-regex
Redact: mark text by caller-supplied regex (#1886)
2026-11-08 13:30:34 +01:00
FredApps 3ba5b28533 Merge pull request #1983 from vangel76/feat/optimize-target-bytes
Optimize: optional target_bytes for doc_optimize (#1889)
2026-11-08 13:22:27 +01:00
FredApps 04f3421c9f Merge pull request #5562 from vangel76/fix/bookmark-rename-select-5529
Select the bookmark title when creating or renaming a bookmark (#5529)
2026-11-08 13:19:34 +01:00
FredApps 7d02602bde Merge pull request #5564 from vangel76/feat/crop-toolbar-5330
Add Crop pages to the document quick toolbar (#5330)
2026-11-08 13:19:28 +01:00
bmh129 4bcc63e059 M10: the Verify screen downloads the web OCR models itself (ocr.ocr-web)
In the web build, Verify's Run OCR and its page/region re-reads never
started the model download. On a fresh page they failed at once with the
desktop's "set PDFCRAFT_MODELS" message, and worked only after Recognize
Text had downloaded the models.

Both now start the download (web_models_missing), say "Downloading the
text recognition models…" and remember what was asked; poll_models runs
it once the models land, or shows "Couldn't download the text
recognition models: {e}" when the download fails. Nothing runs if the
screen was closed meanwhile, and an engine the browser can never run
says why instead of waiting.

Tests: models_landed starts a waiting run and a waiting re-read, reports
a failed download without touching the review, and runs nothing for a
closed screen. The first three fail without the fix. Checked by hand in
Chrome: a fresh page, Verify > Run OCR downloads the models and reads
the page (47 words on a real scan); with the models missing it shows
the HTTP 404 and a second click after restoring them works.
2026-11-07 21:35:59 -06:00
bmh129 ce25eb2aef M10: web OCR docs, parity and roadmap after the port onto #977 (ocr.ocr-web)
Hosting notes for the models and workers, the platform parity text, the
ocr.ocr-web parity entry (now citing the ported tests, with the known
limits: the page runs the cleaning steps, the Verify screen re-reads on
the page, ocrs only), and the roadmap logs.
2026-11-07 21:35:59 -06:00
Chiranjeevi U adbf1dc01f M9: signed appearances show their real validation state (#79) (#2808)
* M9: redraw named-layer signature status layers

- /N drawing one Form that holds n1 and n3: n1, n3 and n4 redrawn for the state
- copies made once per original and state; originals never edited
- Lucide icons; captions in Helvetica, embedded Inter, or craft-fonts outlines

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: draw signature states in the display copy, exports and print

- marks follow the bytes shown; a changed verdict redraws after a metadata edit
- background opens pick up the trust and captions current at commit

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: status captions in the interface language

- the three captions in all 22 catalogues
- Signatures panel colours from sign::status_rgb

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: signature badge in the widget's corner

- a quarter of the shorter side, 48pt cap; outline only under 32pt
- dropped for a drawn valid state; bottom-right where the appearance draws one
- sign_list reports verdict_shown; parity and roadmap

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: keep Arabic signature captions in logical order

Pass raw RTL catalogue text to the signature appearance shaper so UAX #9 is applied exactly once. This keeps the rendered word order and exported /ActualText consistent.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M2: read /ActualText over painted paths and pictures

- A replacement that marks no glyph reads where it paints, cut to what is visible and along the way it is drawn.
- A span the page content leaves open ends before the annotations.
- OCR and scan field detection still count such a page as having no text of its own.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M8: redaction strips /ActualText around paint it clips

- A path or shading written again under a clip counts as changed, so a partly redacted word drawn as outlines leaves no name in the file.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: recognise only layer forms that draw n1 and n3

- Settle on the one form holding both first, then read its content once per call.
- Skip an /N that is its own layer form; copies keep the layer's /OC.
- A verdict counts as drawn when any visible widget shows it.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: keep the signature badge and redraw wherever a state changes

- The badge stays for every state; its mark sits bottom-right as the widget lies on the page.
- An edit that changes a verdict redraws every page; captions apply before startup files open.
- Print, the CLI's render and the repair log follow the display copy.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: restore the attribution usage the rebase dropped

- Usage lines for the three icons, Inter and the craft-fonts input; ATTRIBUTION.md rebuilt from them.
- Roadmap and localization notes for this round.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: pin signature field names on a nested form

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: bound the signature field walk

- kids share their parent's name and type instead of each copying them
- a widget stays listed under every field that claims it, as one shared copy
- a field written in place in a shared /Kids array is walked once
- caps of 20,000 fields, 100,000 entries and 4096-byte names; no states drawn past them

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: keep a document signed when the field walk stops early

- list_cached adds a signed row of unknown validity for the fields the walk didn't read
- Save As and actions then append, and Optimize, Sanitize and full rewrites are refused, as for any signed file

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M8: removing hidden content no longer strips the names of shadings

- With no regions to clip, a shading is left alone instead of wrapped and marked clipped
- clean_replacements skips scopes already marked, so deep nesting stays linear

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M2: read a picture's /ActualText over the whole picture

- draw_image's transform maps pixels, not a unit square, so the text sat on one pixel in the corner; the test now draws a 2x2 picture
- Painted grows in place instead of being copied through an Option

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: cap the bytes of names the signature field walk keeps

- A kid's name repeats its parent's, so one long name over 20,000 kids came to 80 MB; the walk now stops at 4 MiB of names and counts the rest as unread
- Cover the widget written into a shared /Kids array and the field cap among kids, which no test caught
- sig_fields is private again, and the cap docs say what happens past each cap

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: skip status-layer work that changes nothing; fix tests that could not fail

- show_status_layers and has_status_layers return before reading the form when nothing is signed, so an unsigned form no longer walks every field on each edit
- revalidate and draw_captions leave documents with no drawn state alone, and no longer copy the signature list to update it
- Scan form detection reads the page's text once; comment and badge tips share one hover rule
- Tests: drop a table row that repeated another, stop taking an expectation from the code under test, stop comparing an Arc with itself, give bare asserts messages

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: test the signature field name cap at the engine boundary

- A 4,096-byte name is read and blocks a full rewrite; at 4,097 bytes the list holds the unread-fields row and a full rewrite is still refused

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: test signature states beside fields the viewer draws

- One display copy holds both a field's drawn appearance and the signature's state.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: draw Thai and Devanagari status captions in their own script

- script_of names Thai and Devanagari, so a craft-fonts face meant for either draws the caption as outlines; without one it stays English.
- Localization, parity and the craft-fonts usage line list the new scripts.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M2: paint left out by an exclusion mixes its /ActualText replacement

- A replacement placed by outlines or pictures, some of them excluded, fails filtered extraction as one with excluded glyphs does.
- Glyphs still place a replacement that has them.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: a long-named field that can't hold a signature leaves the walk whole

- A leaf of another type passed over for its name is no signature field and has none below it, so an unsigned form stays unsigned.
- A signature field, or any field with kids, still counts as unread.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: status captions use document faces only

- Each script takes the face PdfCraft writes it into documents with, so Chinese stays English although the pinned craft-fonts' interface-only Chinese face is meant for Hani.
- The craft-fonts usage line names Devanagari: the pinned revision has a face for it, and Hindi captions are drawn in it.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: refuse a new signature field the field walk can't read back

- Signing adds a field only when the walk reads the whole form with it: past a cap the signature would be listed as unread fields, and its name could repeat an unread field's.
- A field the walk already reads is signed as before.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: refuse a new signature field with a blank name

- The walk lists no field without a name, so signing now also requires the walk to list the new field: one named only with spaces or NULs would be a signature nothing shows.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: an unnamed signed field or an unreadable form counts as unread

- A signed field with no usable /T, or an /AcroForm or /Fields of the wrong type, cuts the field walk short: the document keeps its unread-fields row and stays signed.
- Signing refuses a blank new field name before it adds the field, so the refusal still says why.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: no display copy when no signature state is drawn

- display_bytes_for makes a copy only when it added an appearance or drew a state, as the app does.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: the verdict mark stays on the page

- A widget past the crop box takes its mark in the part on the page, or none when too little of it is there.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: typed values stay drawn while a signature redraws every page

- A form edit that changes a signature's state puts its values back in unbaked after the pages are invalidated.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: the action, Save As and shading tests check their effects

- The signed action and Save As tests reopen the output for the title and the comment; the shading test checks its clip.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: values typed earlier stay drawn while a signature redraws every page

- A form edit that changes a signature's state keeps every value still waiting on its page, not only its own.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: comment and content edits keep typed values drawn too

- An edit to one page's comments or content that changes a signature's state moves no page, so the values still waiting on their pages stay pending, as after a form edit.
- The pending-value test runs a field deletion and a comment after a fill, under a certification that allows the fill; its messages say what it checks.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: the language-change test skips without catalogs

- Like the other language tests, it returns early when the build has no craft-i18n catalogs.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: signed fields behind kids without a name count

- A kid with no /T but kids of its own is walked as a field node, so a signed field under it is listed by its full name.
- An unnamed kid claiming a signature its field doesn't have cuts the walk short, so the document stays signed and keeps its unread-fields row.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

---------

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
2026-11-07 20:37:17 -06:00
Tiago KielandClaude Opus 5.5 82116a5c7f fix(edit): an edited OCR phrase is drawn at one size (refs #3313)
Recognize text sizes each word from its own box and a guess at how much
of the em its letters fill, so one phrase's words come out several
points apart (9 to 15.6 pt on a LaTeX scan). Editing a phrase drew and
saved every word at its own size.

An invisible block's size is now the median of its words' sizes
(TextBlock::phrase_size). The styled rewrite draws every run at it and
scales each word's width calibration by its own size over the phrase's,
so words keep the width the scan shows them at; the single-style
rewrite (a new family or bold) uses it in place of the first word's
size; and the inline editor lays each word out at it while typing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:35:47 -05:00
FredApps 33e3945c04 Merge main and preserve unreadable diagnostics with cached failures 2026-11-07 15:48:09 +01:00
FredApps a94e90da65 Merge pull request #6471 from ZIDAN44/fix/6440-add-text-cmyk-gray
Add text: gray or CMYK colour, painted with g or k (#6440)
2026-11-07 15:39:04 +01:00
vangel76andClaude Sonnet 5.5 81af7f1cd9 Merge upstream/main into feat/redact-mark-regex
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 11:54:30 +01:00
xullul 7634ac8064 M7: address review of grouped Thai text preparation
Add:
- Report text grouped more than eight levels deep instead of leaving it
  silently grouped, and refuse forms whose resource-naming operators carry
  surplus operands, which would otherwise lose nested artwork when inlined.
- Return the applied preparation's own counts from Session::prepare_text, so
  text_prepare no longer reports a separate preview's.
- Shape a THSarabunPSK program that has its own Thai mark positioning by that
  layout, in its own codes. A span the subsets can't show in place is shaped
  whole, Latin rows included, by a complete program of the same face and style,
  such as a CFF face from Load font.

Change:
- Expand only forms that show text, directly or through nested forms; image-
  and vector-only forms stay grouped and uncounted. Match a Do's raw resource
  name bytes, and refuse RGB-blended groups on pages naming no blending space.
- Check the recovered THSarabunPSK map and the Windows PUA alternates glyph by
  glyph against the subset's advances, leave dropped glyphs unmapped, and
  insert the repair after the last endcmap.
- Shape a Thai run split across same-face subsets cluster by cluster and skip
  subset glyphs that are empty. Take the legacy path only for programs in the
  legacy glyph order, needing only the glyphs the new text uses.
- Require GPOS mark/mkmk in the script and language system HarfRust selects.
- Match same-face candidates by weight and slant from the name, descriptor
  weight or a non-zero ItalicAngle; the Italic flag alone is ignored, as
  exporters set it on upright faces. Never complete bold or italic text with
  the Regular bundled Thai face.
- Name the missing character, or the unavailable builtin:thai choice, in errors.

Chores:
- Document text_prepare's fields, builtin:thai, the app's refusal reporting
  limits and the new routing; extend the text-edit parity evidence. Tests that
  need craft-fonts now say they skipped instead of returning silently.

Validation: with and without craft-fonts, fonts 105 (+3 build-input), edit
465, engine 482 (+28 integration) and automation 50 + 352 passed; editor flows
248 passed. Formatting, strict Clippy, layers, assets and parity passed; the
wasm check was not run locally. Each new regression failed with its fix
reverted. Real-PDF edits on Royal Gazette 26288 kept their source fonts.
2026-11-07 15:59:38 +07:00
xullul c7e8d127af M7: edit grouped Thai text while preserving source formatting
Add:
- Prepare supported Form placements as original page operators through the
  existing-text UI and text_prepare, with shared-form isolation and undo/redo.
- Repair narrowly identified Thai exporter maps on copied page fonts, recording
  repairs without changing the embedded source glyph programs.

Change:
- Reuse legacy Thai Regular/Bold glyph codes and same-face punctuation subsets;
  retain point size, spacing, horizontal scale, rise, colour and paint settings.
- Keep cross-family Thai replacement explicit and write matching bundled Thai
  subsets through the existing bounded outline path rather than static embedding.
- Keep replacement-only menu choices out of the source-font preview atlas so
  opening an ordinary paragraph does not wait an extra frame for unused fonts.
- Refuse unsafe compositing, tagged or malformed groups, and missing contextual
  glyphs without losing the user's document or changing other placements.

Chores:
- Document the editing limits and headless workflow; keep text-edit parity partial.

Builds on dekkeng's Thai fallback work in #2811 (fba0a29a) and mtlcl410's Form
Ungroup implementation in #3067 (6eafca9c), adapted to the current module layout.
The Ungroup menu and older UI font-priority changes are not included.

Refs #3185, #2964 and #2142. Synthetic regressions cover source-font preservation,
shared forms, unsafe refusals, layout, undo/redo, rendering and save/reopen.

Validation: library suites 1,675 passed; automation integration 331 passed;
document-font integration 2 passed; editor flows 248 passed. Existing ignored
tests remain. Formatting, scoped strict Clippy, layers, assets, parity and wasm
checks passed. The unchanged opacity regression failed before the choice-only
atlas correction and passed afterwards.
2026-11-07 12:27:29 +07:00
Zinadin Zidan 7e8551e4bc Add text: gray or CMYK colour, painted with g or k (#6440)
page_add_text and content_update took only RGB, so text added to a CMYK
print file painted 0 0 0 rg, rich black on press. They now also take gray
(0 to 1) or cmyk ([c, m, y, k]), painted with g or k and kept in /PCAdded as
/Gray or /CMYK beside /Color, which holds the matching look. An ink applies
only while the colour still shows its look: picking another colour, also in
the Format panel, drops it for good. RGB text is written byte for byte as
before, and a stored ink that doesn't match its colour is ignored.
2026-11-07 01:01:33 +06:00
KenM76andClaude Opus 5.5 cf3ee49fbc Merge origin/main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-06 06:54:33 -05:00
KenM76andClaude Opus 5.5 1107834209 Merge origin/main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-06 06:03:59 -05:00
LetoCoder26 fbe079c953 fix(sign): compare the catalog's /Perms by content under P=1
Rebased #5173 onto 9afa1266. Its later-signature predicate is superseded by
#6364's object-based check, so only the /Perms rule lands: under DocMDP P=1 a
/Perms that differs from the signed catalog's, by content, is a structure
change however it was rewritten.

Co-Authored-By: LetoCoder26 <leto@lvanwesten.eu>
2026-11-05 21:43:14 -05:00
9f04acccef M10: OCR engines, confidence, preprocessing and a review workflow (#977)
* M10.20: plan Unicode text for OCR output in Type3 faces (fonts, edit)

OCR output used to flatten every character beyond WinAnsi to a silent `?`. This
adds the two pieces that let the OCR layers write the recognized words as they
are; the OCR code that uses them follows in M10.21.

pdfcraft-fonts
- `TextPlan::for_words` maps each word to the standard Helvetica when WinAnsi
  covers it, else to Type3 fonts drawn from the craft-fonts faces (the Japanese
  document face first, then the rest of the build input). Characters no face
  has a glyph for are counted (`missing`), never dropped silently. At most 240
  characters per font and 2400 per plan, so one hostile word cannot balloon.
- Which face has which character is asked once per (face, character) for a plan,
  not once per word.
- `face_has`, `craft_glyph`, `win_ansi_char`, `fits_win_ansi` are public for it.

pdfcraft-edit
- `add_type3_fonts` / `stamp_with_fonts` register a plan's fonts on a page under
  a free `PCOcr`, `PCOcr1`, ... name, build each font once and share the object
  with later pages. A shared face is registered under a free name on a later
  page that already has a font of the name it was given first, instead of
  replacing that page's own font.
- The fonts carry real advances and a ToUnicode CMap, so extraction reads the
  text back.

pdfcraft-redact
- A test shows a hand-built Type3 text layer (the shape the OCR output writes) is
  redacted like any other text and the verifier passes.

Tests: pdfcraft-fonts 62 passed, pdfcraft-edit 129 passed, pdfcraft-redact 33
passed; clippy -D warnings clean on the three crates. The name-reuse and the
redaction tests build their fonts by hand, so they run without the craft-fonts
build input; the tests that plan real faces skip without it and were not run in
this commit's own check.

* M12: give the hostile-XFA open test more room on a loaded machine

The test opens a form whose calculate script sets a value 50 000 times, against
the one-second budget of an unasked script and a five-second wall-clock bound
on the open. Both sit close to what a machine running several suites at once
can take, so the test can fail without anything being wrong.

It now runs 20 000 iterations (the total is 19999) and allows 30 seconds, which
still fails for a script that hangs or is not capped. Test-only; unrelated to
the OCR work in this series, so it is a commit of its own.

Tests: both hostile_xfa tests in pdfcraft-engine pass.

* M10.21: pluggable OCR engines, preprocessing, review sessions and the OCR tools (ocr, engine, create, automation)

Scan & OCR gets a second engine, optional raster clean-up, a review step before
anything is applied, editable output and a batch mode, headlessly first: the
interface for them follows in M10.22.

pdfcraft-ocr
- A `Recognizer` trait with two engines: the built-in ocrs (moved out of lib.rs)
  and the installed tesseract program, run as a fenced external process: fixed
  argv, allow-listed language codes, a private temp directory (mode 0700 on
  Unix), 120 s timeout, 64 MiB output cap, 50 000-word reading cap. A location
  from PDFCRAFT_TESSERACT is validated (absolute, existing, executable) and an
  invalid one fails closed instead of falling back to the PATH. `--list-langs`
  and `--version` are read from stdout or, for older versions, stderr.
- Strategies: PrimaryOnly, ConfidenceWeighted (the second engine joins when the
  first reading's mean is under 70 or unknown) and a ROVER merge (higher
  confidence wins per word; against an ocrs word of unknown confidence a
  tesseract word wins only at 70 or more; capped at 100 M comparisons; a failing
  partner degrades to the primary with a note).
- Preprocessing: opt-in auto-rotate, deskew, denoise and binarize on the RGBA
  raster, each with an exact inverse so word boxes map back to the scan. The
  300 dpi normalization the steps are calibrated for runs only when a step is
  on: with every step off the render is read as rendered, as before. (Enlarging
  a 72 dpi render 4x on its own made the models read "due: €250" as "ue: ?250".)
  `OcrImage` is validated by construction (side and 64 MP area caps, checked
  before any buffer is allocated, including by the resize).
- Confidence bands (High >= 90, Medium 70-89, Low < 70) drive review.

pdfcraft-engine, pdfcraft-create
- `OcrSettings` gains engine, strategy, the four steps, output mode, region, the
  skip flags and the low-confidence threshold; Automatic reads confidence
  weighted unless a strategy is chosen.
- A recognition is a session: the document guard refuses a stale result with its
  reason, pages can be applied one at a time (the guard is taken again after each
  applied page), and applying is one undoable step. A per-language dictionary
  (a plain-text file: words added from any document stay on disk after it closes).
- Output is searchable (an invisible layer) or a NEW editable document of visible
  text at the recognized boxes (`create::from_contents`); words beyond WinAnsi use
  the Type3 faces of M10.20 in both, with the shared plan across pages.
- Engine discovery never blocks the interface: results are cached, a stale
  answer is shown while a background probe retries, and the models search and
  `--version` are cached. Batch: pages or files that already have text are
  skipped unless forced, and inputs sharing a file name are found up front.

pdfcraft-automation
- ocr_recognize (engine, strategy, steps, region, output mode), ocr_words (with a
  retained review handle), ocr_review_apply, ocr_dictionary, ocr_status and
  ocr_recognize_files. The editable `out` must be a new file inside --root and is
  refused for a searchable run (it used to be ignored), two batch inputs of one
  file name are refused before anything is read, and the batch folder is checked
  for existing outputs.

pdfcraft-ui-egui
- Only what the new API needs: the Recognize Text dialog reads with
  `recognizers(&settings)`. `ocr.correct` is registered with its label in every
  catalog; the screen itself is M10.22.

Tests: pdfcraft-ocr 78 passed; pdfcraft-create passed; pdfcraft-engine and
pdfcraft-automation 450 passed and 1 failed (the automation lib 17, automation.rs
157, hostile_args 2 among them); pdfcraft-ui-egui lib + ocr + control tests 391
passed; clippy -D warnings clean on the five crates. The one failure is in a
test this commit does not change:
recognize_text_makes_a_scanned_page_searchable fails where the models are
installed (they read "quick" as "auick" at that render size); the roadmap on main
already records it. The real tesseract binary ran in
tesseract::tests::recognize_runs_the_installed_binary (tesseract 5.4.0, PATH).

* M10.22: OCR Verify screen, the Recognize Text options and the interface catalogs (ui-egui)

Scan & OCR ▸ Correct recognized text: a full-window screen where a recognition
is reviewed before anything is applied, plus the dialog and batch options for
the engines of M10.21.

OCR Verify screen
- Four panes: the page list, the source page with confidence-coloured word boxes
  (click to select, drag to draw a region), the recognized text, and a zoom plus
  word inspector. Corrections commit on Enter (an empty commit removes the word),
  Remove/Restore, "+ Dictionary" (a per-language file in the preferences folder,
  so a word added from a document stays on disk after it closes), per-word and
  per-page Verified, F8 / Shift+F8 walk the low-confidence words, Ctrl+T marks the
  page reviewed, Ctrl+Tab moves the pane focus, and review edits undo within the
  panel.
- Re-OCR of the whole page or a drawn region. A region re-read moves the review
  marks of the words it keeps to their new positions and selects the first new
  word; a whole-page re-read clears that page's marks (and the undo brings them
  back). The region outline is scaled onto the pane, not mapped through user
  space again, so it is right on rotated and cropped pages.
- Accept / Reject per page or whole. Pages accepted one at a time each apply once
  (the document guard is taken again after each applied page; an edit made
  elsewhere in between is still refused with its reason), and Accept in
  searchable mode is one undoable step. Editable mode writes a NEW
  `<name>_ocr.pdf` through a save dialog and never the source. A real Cancel
  keeps the pages already read.
- The engine and strategy menus disable a choice that cannot run and say why;
  the check never waits on a process (a background probe fills a cache), and
  choosing Automatic moves "Primary only" to Confidence weighted.

Recognize Text dialog and batch
- Engine, strategy, the four clean-up steps and the skip flags are remembered with
  the other choices (a saved Automatic without a strategy reads confidence
  weighted). The batch refuses two inputs of one file name and existing outputs
  before reading anything, notes low-confidence pages at the settings' threshold,
  and its toast has a Cancel (the file in flight finishes).

Catalogs and attribution
- The 90 new interface strings are in all 21 catalogs (the Lithuanian ones were
  written without native review, and the Bulgarian ones avoid U+045D, which the
  bundled monospace face lacks). ATTRIBUTION.toml carries the new hashes, a
  note on the Lithuanian entries and the README screenshot; ATTRIBUTION.md is
  regenerated.
- README: a screenshot of the Verify screen, taken with
  `cargo xtask screenshots` from a synthetic scan (`shot --ocr-verify on`) built
  from allowed assets; the page reads it with the real engines.

Tests: pdfcraft-ui-egui lib 373 passed (1 ignored: the Windows hinting
measurement probe), tests ocr 6, control 46, fonts 9; clippy -D warnings clean on
the engine, ui-egui, pdfcraft and xtask packages; `cargo xtask assets` ok. The
review state machine has unit tests for every fix above; each was seen failing
with its fix reverted.

* M10.23: parity entries, roadmap and localization notes for the OCR workflow

Records what M10.20-M10.22 ship, and what they do not.

parity/acrobat-features.toml
- Status changes against main: ocr.rotation-detection and ocr.deskew planned ->
  shipped (the opt-in steps of M10.21, with their tests); ocr.correct-recognized-
  text and ocr.ocr-engines planned -> partial. Other Area I entries get their
  evidence and notes brought up to date.
- ocr.correct-recognized-text: the evidence includes the one-page-at-a-time accept
  and the region re-read tests; the notes say what the headless tools cover
  (ocr_words with a retained review, ocr_review_apply through the same stale-
  document guard, ocr_dictionary) and what is still UI-only (suggestions,
  per-word Verified marks, region re-reads).
- ocr.ocr-engines stays partial rather than shipped: part of its evidence is
  conditional (the real-binary test runs only where tesseract is installed and
  skips elsewhere, and the process-level tests are Unix-only). It cites the tests
  that always run (location validation and the fail-closed rule, --list-langs
  parsing from stdout or stderr, the merge rules, the default strategy) and says
  which part is not proven everywhere.
- `cargo xtask parity`: area I 32.7% -> 44.2% weighted (5 -> 7 shipped, 7 -> 9
  partial); overall 52.8% -> 53.0% shipped and 58.2% -> 58.5% weighted.

ROADMAP.md
- One progress-log entry (2026-10-13, newest first) and the banner line. It says
  the catalogs are 21 with the Lithuanian additions not natively reviewed, that
  the end-to-end redaction test of the Type3 layer needs the craft-fonts build
  input, and that recognize_text_makes_a_scanned_page_searchable still fails where
  the models are installed, as main's log already records.

docs/localization.md, docs/localization-parity.md
- The Lithuanian entry count (2,578 -> 2,668) and the counts of the other
  catalogs that carry entry counts are re-measured with the 90 new entries; the
  Lithuanian OCR entries are marked as not natively reviewed.

Checked: `cargo xtask parity` (numbers above, evidence tests exist) and
`cargo xtask assets` pass on this tree.

* M10.24: ocr: apply a late result only to the document it was read from

The Recognize Text dialog closes on Go, so the document can be edited, or have
text redacted, while a long run is still reading. The result was applied
without the stale check the tools and the Verify screen use, so words read
before a redaction could be written back as invisible text. The run now keeps
its guard and applies through the checked path.

Accept with the editable output in the Verify screen had the same gap (Exit
keeps the review open): it now refuses when the document changed since the
review started.

Both new tests fail with their guard switched off.

* M10.25: ocr: ocr_dictionary takes dictionary file names only

Updating sorts, lower-cases and rewrites the file it is given, so pointed at
.env or a README it would corrupt it. The tool now accepts only
ocr-dictionary-<language>.txt style names, for reading as well as writing.

The size-limit test used a file name the rule now refuses first, so it renamed
its fixture to keep testing the size limit. The new test fails with the rule
switched off.

* M10.26: pt-BR: keep (s) on the word counts and say mecanismo for engine

Two review fixes from a native speaker: the two word(s) lines had lost their
(s), so a count of 1 read as 1 palavras, and engine was translated both as
motor and mecanismo. Mecanismo is the Brazilian word for a software engine and
two lines already used it, so the other six now match.

* M10: fit the rebased OCR workflow to current main's structure

* Fix OCR workflow issues from the review

Honor the Recognize dialog's editable-output choice: its result opens in
the review screen (whose Accept writes the editable document) instead of
an invisible layer. Make page acceptance idempotent: a second Accept
entire page never appends the OCR layer twice and keeps later
corrections. Move page and region re-OCR onto a worker thread with a
status line and a cancel that discards the reading. Skip the Tesseract
probe for jobs that name their engines. Bound Tesseract's stderr while
the child runs, not only after it exits. Surface the editable output's
substitution notes in the tools' and the run's results.

Also repair the OCR batch tool against the current structure: inputs are
held to the read root again, and a batch whose inputs share an output
name is refused before the output folder is created, compared the way
the default filesystem compares names; and the editable writer test's
word stays inside the fixture's pages.

* Apply pre-review fixes to the OCR workflow

Hold the automation OCR tools to the document's copying permission, as
the other text tools are: the recognized text is left out of the
results when copying is forbidden, and the paths that carry the words
out of the document (the editable output, the review word list) are
refused. Mark the ocr_dictionary tool as overwriting its path. Close
the searchable layer's graphics state around its invisible text so the
rendering mode cannot leak into later content. Read the OCR user
dictionary folder from the portable data folder in portable mode, like
the recovery store does. Resolve the attribution manifest's leftover
conflict markers and regenerate the generated attribution. Describe the
review flow, the searchable layer's Type3 fonts and the parity evidence
references accurately, and drop an undeclared copy of the recovery
module.

* OCR: reject readings that exceed the word limit

Assisted-by: OpenAI Codex

* OCR: new documents go through the page tree builder main added

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* OCR: review notes and lints

The editable output refuses a file created during recognition. ocr_words takes
skip_text_pages and force_ocr. Automatic OCR checks the engine it will use. The
stderr test stays below the kill cap, and a failed --ocr-verify shot exits with
an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* OCR: restore the Verify screenshot attribution, follow the moved constants

The merge dropped the screenshot row with the translation rows. The model
filename constants live in ocrs.rs now, so the license check reads them there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Attribution: the Verify screenshot entry goes after the header comment

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Attribution: the screenshot row in the table follows the manifest order

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:39:55 +01:00
b3a90f07f3 Parity: titles and the PDF/X row say only what is implemented (#6300)
* Parity: titles and the PDF/X row say only what is implemented

* Parity: the PDF/X row says removal matches the pdfxid: prefix only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:32:34 +01:00
eliets2andeliets2 ae893c1517 M13 batch: correct parity status and scope the no-replace sentence (#6299)
Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com>
2026-11-05 20:13:45 +01:00
496e37d72b M5: text boxes and typed text take an oval or a circle for their border (#4543)
* Fill & Sign: a Border control for typed text, and a border colour of its own

Rebased onto main as one commit: main's Fill & Sign text colour (#2444) runs through the
same places as the border (the open box, typed(), the page input, the toolbar and the
preferences), so every step of the branch met it again. Text colour and border now travel
together as TypedLook (typed_with), which keeps typed() and the new function under Clippy's
argument limit; a border without its own colour takes the text's. Make Current Properties
Default on typed text also keeps its colour for new text now.

The branch's commits, in order:
- Typewriter text: a border change grows its box on every side, so the text keeps its place
- Fill & Sign: a Border control for typed text and dates
- Parity: the typewriter border's evidence
- i18n: the Border label in every catalog, recorded in ATTRIBUTION.toml
- Text boxes, typewriter text and callouts: a border colour of their own
- Fill & Sign: the toolbar follows the selected text; a Border colour button
- fill_sign_add and comment_edit take `border_color`
- Parity: the border colour and the toolbar following the selection
- i18n: Border colour and Same as text in every catalog, recorded in ATTRIBUTION.toml
- Typed text's border: /Border's width is read, a non-finite border colour refused, a saved Same as text restored
- Fill & Sign: the toolbar changes only the selected text; Make Current Properties Default keeps new text's border
- Review: Make Properties Default gives a text box's or callout's tool its border colour
- i18n: the new labels in the Persian catalog, recorded in ATTRIBUTION.toml
- Review: a test that dragging the Font size control resizes the selected text to where the drag ends
- Rebase: the typed text's border in main's annot and comments modules (Style's border colour, exports), and craft_ui's ComboBox

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M5: text boxes and typed text take an oval or a circle for their border

Rebased onto #4146's new head as one commit (see there: main's Fill & Sign text colour runs
through the same places). The border shape goes into TypedLook's border with the width and
colour; a circle around typed text still takes the snug box.

The branch's commits, in order:
- Text boxes and typed text take an oval or a circle for their border
- A circle goes snug around its text
- Review: fill_sign_add takes an oval or a circle only with a border
- Review: a circle made on a quarter-turned page is sized from the text as shown
- Review: Border ▸ None takes an oval or a circle away with the line
- Review: the Border colour button shows the selected text box's own colour
- i18n: Border shape in the Persian catalog, recorded in ATTRIBUTION.toml
- Review: Border ▸ None on a circled text is one undo step
- Rebase: the border shape in main's annot, engine and comments modules; craft_ui's ComboBox; Fill & Sign's border controls in a function of their own (main's complexity limit)
- Review: Thickness 0 in Comment Properties takes an oval or a circle away, as Border ▸ None does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Open the Font size popup first in the drag test: main moved the size into a popup

* Border shape per selected comment, and split the oval/circle border out of draw_free_text (clippy cognitive complexity)

* Re-run CI (the push of the main merge did not start it)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 17:32:01 +01:00