Commit Graph
4675 Commits
Author SHA1 Message Date
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 0f274278d7 Fill & Sign test checks Escape writes nothing by edits, not saved bytes (#6646)
The test compared two saves' bytes, which carry the save time, so it failed
when the saves landed in different seconds (seen on CI). It now checks that
the edit generation, the unsaved state and the undo step are unchanged and
that the one annotation is all there is.
2026-11-09 04:09:13 +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 ea239829e4 Export tests: a two-column page and a quoted poem keep their lines apart
Review asked for the near misses of joining wrapped lines. A short line,
a numbered item and a symbol-font bullet were already covered; these add
a line beside another column and a poem between paragraphs.
2026-11-09 01:18:37 +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
FredApps 1afbc1254a Merge pull request #6525 from chamber074/fix/redact-nan-quads
redact: treat unusable /QuadPoints as absent in every reader
2026-11-08 19:06:24 +01:00
bflatastic c7fc7560eb Test Thai word grouping and row filling without a Thai face
thai_lines' row filling moves into wrap_words, so both it and thai_words are
tested with synthetic advances where no Thai face is built in.
2026-11-09 00:54:58 +07:00
BFlat 83c67803da Merge pull request #6627 from storytold/fix/thai-grouped-text
M7: edit grouped Thai text with one click, on Ungroup's form expansion (#6249 + review fixes)
2026-11-09 01:51:21 +08:00
bflatastic f6831d58ea Merge remote-tracking branch 'origin/main' into merge/3492-thai-line-breaks 2026-11-09 00:15:27 +07:00
BFlat c17638e050 House rules: copy the licence allowlist with Unicode-3.0 from craft-contrib (#6635)
storytold/craft-contrib#40 (RFC-0026) added Unicode-3.0 to the shared list.
2026-11-09 01:15:21 +08: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 ec1f954908 House rules: copy craft-contrib's licence allowlist with Unicode-3.0
storytold/craft-contrib#40 (RFC-0026) added Unicode-3.0 to the shared list.
2026-11-08 23:53:42 +07:00
bflatastic 378ef84ea1 Keep a legacy Thai alternate out of the nominal mark's place
A legacy THSarabunPSK subset whose map gives a shifted alternate (glyphs
342-368) as the nominal mark, its nominal glyph dropped, shaped that
alternate wherever the nominal was needed, drawing the mark out of place.
The shaping copy now reaches an alternate only through its presentation
form, so text that needs the dropped nominal is refused instead.
2026-11-08 23:51:51 +07:00
bflatastic 5f265960ad Let only a same-style complete program keep the bundled Thai face out
A complete program of the source's family in another style, such as one
its descriptor marks bold, stopped the bundled Regular Thai outlines from
completing a Regular subset, and the text was then drawn in that bold
program. Count only a program in the source's style, as the subset
completion itself does.
2026-11-08 23:51:51 +07:00
bflatastic e8322a8fc2 Shape Thai digits and bare letters without mark positioning
The source shaper refused any Thai text in a program without GPOS mark
positioning, so a row of Thai digits failed although nothing in it is set
on a base. Require the positioning only for text with a vowel or tone
mark, or Sara Am.
2026-11-08 23:51:51 +07:00
bflatastic d0c56fdfee Refuse the form whose merged resources pass the page's limit
The resource limit was checked before a form's resources were merged into
the page's, so one form could take the page past it and every later form
was then refused, even one whose resources were all the page's already.
Check the merged dictionary instead.
2026-11-08 23:51:51 +07:00
bflatastic f4d669b31f Pass over a complete Thai program that may not be embedded again
The first program of the face that could shape a span was chosen even when
its embedding permissions forbid embedding it, and the edit then failed
while a later program of the same face could have been used. Check that a
program may be embedded before choosing it.
2026-11-08 23:51:51 +07:00
bflatastic 5f356ade12 Give an unencoded space in legacy Thai the source font's space fallback
A space that no subset of the face encodes was drawn as a quarter-em gap,
narrower than the text-space fallback the source font uses elsewhere.
Keep each subset's source space with it and use that for the gap.
2026-11-08 23:51:51 +07:00
bflatastic fbc2dc77a7 Read each THSarabunPSK subset of an edit once, within one budget
Every resource name of a page's Thai subsets decoded and kept its own copy
of the font program, so many names of one large program could hold about
2 GiB for a single edit. Names of one font object now share its program,
the subsets of an edit are decoded within a shared 64 MiB allowance, and
an edit that needs more is refused with the reason, changing nothing.
2026-11-08 23:51:51 +07:00
bflatastic dff6c2cc3e Reach a THSarabunPSK subset's Thai glyphs when its own cmap is Latin-only
A legacy subset can keep a cmap of its own that maps only Latin, so the
editor program returned it unchanged and an edit such as a consonant with a
tone mark failed although the subset keeps both outlines. Add the PDF's
verified Thai and alternate mappings to the editor-only cmap where the
program's own lacks them, keeping its existing mappings.
2026-11-08 23:51:51 +07:00
FredApps 8d4f2db5b1 Merge branch 'main' into fix/redact-nan-quads 2026-11-08 17:51:37 +01:00
bflatastic a64a7b3e55 Test that only an OCR phrase with a usable size is set in one size
Visible text keeps its runs' sizes, and a phrase whose size is NaN, zero,
negative or infinite falls back to its words' sizes.
2026-11-08 23:42:45 +07:00
bflatastic 2f492db9a3 Merge remote-tracking branch 'origin/main' into merge/5454-ocr-phrase-size 2026-11-08 23:39:29 +07:00
skylar taylor-barrick 883ab41bca Merge pull request #6514 from songzhihui/fix/windows-pdf-stream-loading
M10: adopt file-backed Windows printing with approved dependencies and the crash fix
2026-11-08 17:39:19 +01:00
bflatastic 82e50e93fd OCR worker checks the page's pixel count without overflowing
width * height * 4 could wrap for sides near u32::MAX and let a short buffer
through as the right size; the product is now checked.
2026-11-08 23:38:59 +07:00
bflatastic 8ea3c638d2 OCR file-job test compares what was read, not the save's timestamp
a_file_job_reads_in_two_halves compared the two saves' bytes, which carry the
modification time, so it failed when the saves landed a second apart.
2026-11-08 23:38:58 +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
bflatastic d351aa2760 Read Thai font programs and CIDToGIDMaps with DecodeParms given by reference
A predictor whose parameters are an indirect object left its row tags in
the decoded font program and glyph map, so the Thai map repair, the glyph
codes and the shaper read the wrong bytes. Resolve the parameters as the
other font readers do, keeping the decode budgets.
2026-11-08 23:23:09 +07: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 cc9f7977bf Repair a page's damaged THSarabunPSK map without grouped 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.
2026-11-08 22:31:14 +07:00
bflatastic 90b88ddcb6 Ungroup reads the Do it replaces and refuses one inside text or a path
Ungroup and text preparation now check the placement in one place, ungroup_ops, which is given
the page's operators and the Do's index:

- The form is looked up by the name the Do writes, byte for byte. Ungroup looked it up by the
  picture's display name, a lossy UTF-8 copy, so a form named /F#FF was 'missing'.
- A Do with more than one operand is refused: the picture was read for its first, the renderer
  draws its last.
- A Do inside BT…ET or an unfinished path is refused. Inline, the form's text objects would be
  nested in the page's, and its clip would take in the page's path. Preparation already refused
  these; Ungroup didn't.
2026-11-08 22:31:14 +07:00
bflatastic 233bc6c932 Ungroup keeps the translucency an unmatched Q doesn't restore
A Q with no q before it restores nothing; the renderer ignores it. The check for translucency at
the Do took it as restoring the opaque default, so a transparency group drawn after /H gs Q was
inlined although it is drawn translucent.
2026-11-08 22:31:14 +07:00
bflatastic 7169b0d6ad Ungroup refuses a form the page's default colour space would remap
A form with resources of its own and no /DefaultRGB (likewise Gray, CMYK) draws its device
colours as they are. Inline, the page's resources apply, and a /DefaultRGB there would remap
them (ISO 32000-2 §8.6.5.6). Only the form having an entry the page lacks was refused; now either
side having one the other doesn't share is.
2026-11-08 22:31:14 +07:00
bflatastic 5850000e41 Prepare grouped text with Ungroup's own form expansion
text/forms/expand.rs was a drifted copy of ungroup.rs. It is gone: text preparation now opens each
placement with crate::ungroup, so Ungroup and preparation share one implementation.

What the copy added is now in ungroup.rs: a Refusal that says whether the form itself or only
this placement is the reason (intrinsic) and whether a rename alone was the problem (renaming),
the shows_text result, merges_without_renaming, refusals for unreadable /Resources and /Group
entries and arrays with a non-number, a cap on resource counts and q nesting, a total size for
pattern copies, and ending an unfinished path with the form. Ungroup's newer behaviour stays:
names are read from the operand the renderer uses, so a surplus operand is renamed correctly
rather than refused; default colour spaces are compared with the page's; and a group blending
in DeviceRGB on a page that names no blending space is drawn inline, as Ungroup does, rather
than refused as the copy did.

What a form draws is now read within one decode allowance shared with the form itself, so a
crafted group can no longer make one Ungroup decode 64 MiB for each of 1024 nested forms. A
group whose content can't be checked is refused; for a form without a group, blend modes inside
don't matter, and content that can't be checked only means preparation can't tell whether it
has text (shows_text is None), which it reports.
2026-11-08 22:31:14 +07:00
bflatastic 62b85edef1 Write the Thai glyph check without a negated is_some_and
Clippy's nonminimal_bool asks for is_none_or: a glyph with no outline range, or an empty one, is skipped as before.
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
BFlat 922f18cd8e Fix the build: page_rules joins content streams with text::joined (#6621)
#5154 and #6289 each passed on their own base; together page_rules read content_streams as pairs after it began returning ContentStream.
2026-11-08 23:27:10 +08:00
FredApps 981193ad41 Merge pull request #6619 from FredApps/fix/rules-content-stream-api
fix(edit): restore page rules after the content stream API change
2026-11-08 15:30:47 +01:00