Commit Graph
477 Commits
Author SHA1 Message Date
regendorf-ux b7cfbd3980 Find and replace text in Edit text mode: a Replace row in the Find box, Replace all as one undo step (#2467) 2026-11-10 15:19:55 +07:00
BFlat f48cf6eeba Merge pull request #6806 from storytold/merge/mixed-script-2778
fix(edit): preserve source fonts in mixed-script paragraphs (#2778, merged with main)
2026-11-10 14:22:36 +07:00
mtlcl410 67cd69050e M7: paragraph edits keep the advance of the text they drop (edit.text-edit) (#2968) 2026-11-10 14:21:29 +07:00
bflatastic 10755a9da9 Merge remote-tracking branch 'origin/main' into merge/mixed-script-2778
# Conflicts:
#	parity/acrobat-features.toml
2026-11-10 10:45:45 +07:00
bflatastic f6fd408419 Merge remote-tracking branch 'origin/main' into merge/fonts-budget-6239
# Conflicts:
#	crates/edit/src/tests.rs
2026-11-10 10:09:08 +07:00
jonathanwxh-cell c6d9c13002 fix(ui): keep added image corner resize responsive and clip out-of-bounds drag preview (#4477, #3938) (#4625) 2026-11-10 10:00:18 +07:00
PerchAnton 8dfa52c9b5 Merge main into PR #2778 preserving feature behavior 2026-11-10 01:16:46 +02:00
Grzegorz MartensandClaude Opus 5.5 4fca5783a7 M7: a Thai edit refuses only a read past its allowance, and reads its subsets' metrics within it
CodeRabbit's review of 674a20899:

- The Thai edit refused when nothing was left of its allowance, so an
  edit whose reads spent it to the byte was refused. It now refuses when
  a read was refused for want of it (`DecodeBudget::refused`).
- `LegacyThaiFont::from_pdf` read its metrics with `from_dict`, outside
  the edit's allowance, and so did the edit for each subset it measures.
  Both now read them within it (`from_dict_within`), and metrics read
  past it adapt nothing: `from_pdf` refuses the font before reading its
  program, and the edit leaves the subset unread and is refused. The
  edit's scan of every font on the page for its face name stays outside
  the allowance, as on `main`: charged, each alias of a font would pay
  again, and other faces would spend what the face's subsets need.
  `MAX_THAI_PROGRAMS` says what it pays for.
- When the `/CIDToGIDMap` a THSarabunPSK subset's Thai mappings are
  rebuilt from was left unread for want of the editor's allowance,
  `composite_glyph_maps` gave nothing, and a subset with a Latin-only
  cmap of its own was listed as it was, without its Thai mappings. The
  editor now lists no copy of it, as for metrics read past the
  allowance.

Tests: `a_thai_edit_that_spends_its_allowance_to_the_byte_goes_through`
(`pdfcraft-edit`: a subset costing exactly the 64 MiB is edited, and
one three bytes past it is refused),
`a_legacy_fonts_metrics_are_paid_for_from_the_edits_allowance` and
`a_map_left_unread_for_the_allowance_lists_no_copy` (`pdfcraft-fonts`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 21:45:29 +01:00
Grzegorz MartensandClaude Opus 5.5 4d6ff291d7 M7: Reads::decode says what a stage past its limit is charged
The Claims review of the description at b7224cbc9. `Reads::decode` said a
stream past its limit is unread, not refused, "when what is left can pay
for its stages". What `DecodeBudget::decode_stages_within` does, and
`a_stage_past_its_limit_charges_what_came_before_and_the_limit` pins:
with more left than what the stages before the stopped one made and the
limit, it is charged those two; otherwise, with more left than the
stages' total (behind one filter, the limit), it is charged that total;
with no more left than that total, it spends all of it and counts as
refused.

`MAX_THAI_PROGRAMS` also names the second read of a subset's program,
for which of its glyphs have outlines, among what it pays for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:52:47 +01:00
Grzegorz MartensandClaude Opus 5.5 92e8e6eff0 M7: Repair Thai maps reads a /CIDToGIDMap given by reference, and takes a missing one as Identity
The Consumers review of ba28458ad..b7224cbc9:

- `recovered_thai_map` read the descendant's /CIDToGIDMap with
  `Dict::name`, on the line after e9142b03b's /Encoding check, so a map
  given by reference made Repair Thai maps pass the font over, and so
  did a missing one, which is Identity (ISO 32000-2 Table 115), as
  `composite_glyph_maps`, `LegacyThaiFont` and the renderer read it. It
  now resolves the map and takes a missing one as Identity.
- `MAX_THAI_PROGRAMS` says what it pays for: the programs, and the CMaps
  and glyph maps read to adapt their cmaps. The metrics read to find and
  measure the subsets aren't charged to it. Its refusal ("MiB of font
  data") still says what it counts.

Test: `recovered_thai_map_checks_every_kept_glyph_against_the_font_program`
recovers the same map with the font's /CIDToGIDMap given by reference and
with none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:48:01 +01:00
Grzegorz MartensandClaude Opus 5.5 b7224cbc9a Merge main again into fix/fonts-decode-budget
A third merge, of ba28458ad, with nothing to resolve. main's commits since
b6cbf9bb3 add no font program, metrics or CMap read in pdfcraft-fonts or
the editor's font code for this PR's allowance to cover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 16:31:54 +01:00
bflatastic 2ce2d3cf81 Merge main into merge/ui-print-optimize2
# Conflicts:
#	crates/automation/src/redact.rs
#	crates/automation/src/tools.rs
#	crates/print/src/lib.rs
#	crates/render/src/inspect.rs
#	crates/render/src/lib.rs
2026-11-09 20:56:15 +07:00
Grzegorz MartensandClaude Opus 5.5 509cf9f9fd M7: the editor reads no metrics for a composite font it can't adapt
The Resources review of 8eead8ecd:

- `embedded_sfnt_within` read a composite font's metrics within the
  editor's allowance before `composite_glyph_maps` refused any `/Encoding`
  but Identity-H, so an Identity-V or UniJIS font spent its ToUnicode and
  `/CIDToGIDMap` for nothing. Its `/Encoding` is now checked first; a
  program with a cmap of its own is kept as it is, as when the rebuild
  finds nothing.
- The Thai edit's allowance, `MAX_THAI_PROGRAMS`, now also pays for the
  CMaps and glyph maps its subsets' metrics and codes are read from: its
  doc and its refusal say "font data", not "font programs".
- `a_symbolic_truetype_font_without_an_encoding_reads_its_glyph_names`
  also reads its font, whose `post` table names its glyphs, within an
  allowance: the program is paid for by the glyph-name reading, by the
  Mac OS Roman one and for the widths, and a byte short of the first the
  names go unread.

Tests: `a_composite_font_that_cant_be_adapted_spends_only_its_program`,
and `adapting_a_programs_cmap_is_paid_for` again expects a composite font
whose codes aren't Identity-H to pay for its program only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 13:36:44 +01:00
Grzegorz MartensandClaude Opus 5.5 d4abe80671 Merge main again into fix/fonts-decode-budget
main's #3962 (symbolic TrueType glyph names, 63a6f8ace and nearby) reads a
symbolic TrueType font's codes by the `post` names of the glyphs its cmap
selects, before the Mac OS Roman reading:

- crates/fonts/src/pdf.rs, `unicode_map`: both readings stay, in main's
  order, each given the metrics read's allowance. The program read main
  added (`symbolic_glyph_texts`) goes through `Reads::decode`, as
  `symbolic_mac_roman_texts` does since the last merge: paid for from the
  allowance, `past_allowance` set when it can't be, behind several
  filters within the stream's own limit.
- crates/fonts/src/pdf/tests.rs: both sides added tests at the same place;
  all are kept.

Tests: `adapting_a_programs_cmap_is_paid_for` now expects the program to
be paid for a fourth time, and `the_symbolic_mac_roman_reading_is_paid_for`
becomes `the_symbolic_readings_of_a_program_are_paid_for`, paying for both
readings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 13:06:32 +01:00
bflatastic 6404602e63 Merge #5075: fix(edit): Office export respects a form's /BBox and a page's re/W/n clip
# Conflicts:
#	crates/edit/src/text/lines.rs
2026-11-09 18:22:16 +07:00
bflatastic a4a600573e Merge #1998: Print production: Convert colours (grayscale, RGB, CMYK by the device formulas)
# Conflicts:
#	crates/automation/tests/it/automation.rs
#	crates/edit/src/lib.rs
#	crates/edit/src/tests.rs
#	crates/engine/src/commands.rs
#	crates/engine/src/lib.rs
#	crates/optimize/tests/optimize.rs
#	crates/ui-egui/src/app.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/state.rs
#	crates/ui-egui/tests/it/editing.rs
2026-11-09 18:17:38 +07:00
bflatastic b6ddc51ea0 Merge #1918: Print production: Add printer marks (dialog, edit, tool)
# Conflicts:
#	crates/engine/src/edit/run.rs
2026-11-09 18:15:36 +07:00
BFlat 9ee213eff7 Fix main's build and tests after the latest merges (#6706) 2026-11-09 19:12:07 +08:00
Osvaldo Duran MelendezandClaude Sonnet 5.5 e0dc822b9c Merge upstream/main into pr/convert-colours
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 06:10:00 -05:00
Osvaldo Duran MelendezandClaude Sonnet 5.5 d1da3905c8 Merge upstream/main into pr/printer-marks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 06:01:48 -05:00
Grzegorz MartensandClaude Opus 5.5 8eead8ecdd Merge main: the font reads main added are paid for from the allowance too
main's Thai work (#6249, #3492) and the Type 3 and symbolic TrueType
metrics changed the code this PR bounds. Each conflict, and each read main
added that this PR's allowance must cover:

- crates/fonts/src/pdf.rs, the editor's program read: this PR's read is
  kept, its stage-scaled limit and its check for an allowance already
  spent; main's read had /DecodeParms by reference too, which it has.
- The editor's loop: main rebuilds a cmap for a composite subset without
  one from its ToUnicode and /CIDToGIDMap, where this PR skipped Type 0.
  main's composite support stays, with the metrics read within the
  editor's allowance (`from_dict_within`) and no copy adapted from metrics
  read past it, as for a simple font. The /CIDToGIDMap the cmap is rebuilt
  from (`composite_glyph_maps`) reads as the font's other streams do
  (`Reads::decode`): within the allowance, behind several filters too.
- Reads main added to the metrics read: a Type 3 font's glyph
  descriptions (`type3_glyph_box`), and the program read that tells a
  symbolic TrueType font's codes as Mac OS Roman
  (`symbolic_mac_roman_texts`). Both are now paid for from the allowance
  the metrics are read within, and a refusal sets `past_allowance`. The
  glyph descriptions keep their own limits (64 KiB each, 4 MiB a font).
- Constants and helpers both sides added beside one another, and the
  tests both appended, are all kept. Callers main added of
  `unicode_map` pass a reading without a total, as `from_dict` does.
- crates/edit/src/rules.rs: the refusal of a page whose content didn't
  all decode stays, and the operators come from main's `text::joined`.

Tests: `adapting_a_programs_cmap_is_paid_for` now expects a composite
font's ToUnicode, and a simple font's program read again for the Mac OS
Roman reading, to be paid for. Four are new:
`adapting_a_composite_subsets_cmap_is_paid_for`,
`a_full_cid_map_rebuilds_a_composite_subsets_cmap_behind_two_filters`,
`type3_glyph_boxes_are_paid_for_from_the_allowance` and
`the_symbolic_mac_roman_reading_is_paid_for`. The fixture of main's test
of a composite subset without a cmap moves into `cmapless_composite`,
which both use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-09 12:00:17 +01:00
mtlcl410 b8d9497b93 M5: cloudy squares, circles and text boxes are drawn (comment.cloud) (#4195) 2026-11-09 18:32:44 +08:00
oracola91 476effc1c8 Text from a justified line keeps one space between words (#5496) 2026-11-09 18:31:47 +08:00
NetterSilver 69571065b9 Split edit-text paragraphs at headings and blank lines (#3642) 2026-11-09 18:23:57 +08:00
mtlcl410 a2ad6b77f8 M7: replacing a line keeps the source font for the words it can show (#2378) (#2955) 2026-11-09 18:22:25 +08:00
mtlcl410 8d9f8d5f30 M5: a reply is not drawn on the page or flattened into it (comment.reply) (#5632) 2026-11-09 18:15:44 +08:00
jonathanwxh-cellandjonathanwxh-cell ab502255cb edit: preserve gap-drawn spaces and indents in subset fonts via /ActualText (#4229) (#4514)
Co-authored-by: jonathanwxh-cell <jonathanwxh-cell@users.noreply.github.com>
2026-11-09 18:13:34 +08:00
regendorf-ux e63736d2e7 Print production: Fix hairlines (dialog, edit, tool) (#1906) 2026-11-09 18:08:46 +08:00
Osvaldo Duran MelendezandClaude Sonnet 5.5 1b3e0e5630 Merge upstream/main into pr/convert-colours
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 22:07:06 -05:00
bflatastic 08c0a59212 Merge main into merge/redact-forms-misc
# Conflicts:
#	ROADMAP.md
#	crates/render/src/text/tests.rs
2026-11-09 09:56:59 +07:00
Ozt-y 9792328d67 fix(edit): preserve annotation optional-content membership in marked content (#3213) (#3275) 2026-11-09 10:14:10 +08:00
midasmatterhorn 98b4761934 edit: a variation selector after existing text draws the base glyph instead of failing the edit (#4899) 2026-11-09 10:09:30 +08:00
bflatastic 8a39399b80 Format merged import lists 2026-11-09 08:22:26 +07:00
bflatastic cf10038885 Merge #6650: fix(edit): editing an OCR phrase redraws only the words it changes (refs #3313)
# Conflicts:
#	crates/edit/src/text.rs
#	crates/edit/src/text_regions.rs
2026-11-09 08:21:42 +07:00
mtlcl410 83a6a8394e M7: Edit text lines set in a Type 3 font are as high as their glyphs (edit.text-edit) (#6571) 2026-11-09 08:57:56 +08:00
Oliver H. 616f245b04 fix(edit): break the row on every Unicode row break (#6295) 2026-11-09 08:54:37 +08:00
Fatih Kunak 2ef2e2b822 fix(fonts): a subset that gives the space no width can't encode one (#6131) (#6158) 2026-11-09 08:44:48 +08:00
Dylan M. Taylor ae21773fef fix(edit): stop rescanning what a loop already holds in wrapping, grouping and flattening (#5963) 2026-11-09 08:40:50 +08:00
Tiago KielandClaude Opus 5.5 19450d7cdf fix(edit): an OCR word edit can't land on the wrong word (refs #3313)
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>
2026-11-08 16:41:17 -05:00
Tiago KielandClaude Opus 5.5 9f5166f297 fix(edit): spaces a deleted word leaves don't redraw the OCR phrase (refs #3313)
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>
2026-11-08 16:10:47 -05:00
Tiago KielandClaude Opus 5.5 a6229de16d fix(edit): review follow-ups to keeping OCR words (refs #3313)
- 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>
2026-11-08 15:43:19 -05:00
Tiago KielandClaude Opus 5.5 4731036cdb fix(edit): editing an OCR phrase redraws only the words it changes (refs #3313)
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>
2026-11-08 14:51:11 -05: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 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
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
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
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