Commit Graph
202 Commits
Author SHA1 Message Date
KiwiKwiandBabaKush 4a83ec87f5 fix(annot): drop Apple Markup's stale copy when a page's annotations change (#2829)
iOS Markup keeps a private copy of a page's markup (/AAPL:PPK, /AAPL:PPKHash on the page,
/AAPL:AKExtras on each annotation) and draws from it. Once /Annots changes, that copy is stale:
iOS 26 drew a Markup signature twice. Adding, deleting or answering a comment now drops the copy,
records the drop in the repair log, and leaves a refused edit untouched.

The change lives in lookup.rs (change_annots), called from create.rs (add) and modify.rs (delete,
reply, mark). Each of the four operations has a regression test on an Apple Markup page.

Co-authored-by: BabaKush <101988920+BabaKush@users.noreply.github.com>
2026-11-10 12:38:55 +01:00
jonathanwxh-cell 466e1971d6 fix(annot,ui-egui): guard comment edits against non-comments and stale state (#3916) (#4673) 2026-11-10 10:01:09 +07:00
bflatastic 85ebbab0ce Fit the landed changes together: modify.rs imports polygon_rect 2026-11-09 18:33:09 +07:00
bflatastic dd72e2e672 Merge #2345: M6: custom stamps can carry a line filled in when they are placed (#1741)
# Conflicts:
#	ROADMAP.md
#	crates/annot/src/lib.rs
2026-11-09 18:23:14 +07:00
bflatastic c638edf9c7 Merge #2137: M5: comment pop-up windows on the page, with connector lines (comment.popups, comment.connector-lines)
# Conflicts:
#	ROADMAP.md
#	crates/annot/src/info.rs
#	crates/annot/src/lib.rs
#	crates/annot/src/modify.rs
#	crates/annot/src/tests.rs
#	crates/automation/src/comments.rs
#	crates/automation/tests/it/automation.rs
#	crates/engine/src/comments.rs
#	crates/render/src/inspect.rs
#	crates/render/src/inspect/annotations.rs
#	crates/render/src/inspect/model.rs
#	crates/render/src/inspect/tests.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/tools/comments/input.rs
#	crates/ui-egui/src/tools/comments/paint.rs
#	crates/ui-egui/src/tools/comments/tests.rs
#	crates/ui-egui/tests/it/comments.rs
2026-11-09 18:22:50 +07:00
bflatastic 88a2ab97ec Merge #4094: M5: a callout's arrow point, knee and text box change on their own (#1152)
# Conflicts:
#	crates/annot/src/info.rs
#	crates/annot/src/modify.rs
#	crates/annot/src/tests.rs
#	crates/automation/src/tools.rs
#	crates/automation/tests/it/automation.rs
#	crates/ui-egui/tests/it/comments.rs
2026-11-09 18:19:26 +07:00
bflatastic 65a64638fc Merge #4365: M5: text boxes and redaction marks without an appearance draw their default border, standard fonts, default style and outline (view.render-annotation-appearances)
# Conflicts:
#	crates/annot/src/tests.rs
2026-11-09 18:18:11 +07:00
bflatastic 5af131db8e Merge #4358: M5: comments in an /RT /Group are one comment, with the primary's attributes (#4357)
# Conflicts:
#	crates/automation/src/comments.rs
#	crates/automation/tests/it/automation.rs
#	crates/render/src/inspect.rs
#	crates/render/src/inspect/annotations.rs
#	crates/render/src/lib.rs
#	crates/ui-egui/src/panels/comments_panel.rs
2026-11-09 18:17:31 +07:00
mtlcl410 b8d9497b93 M5: cloudy squares, circles and text boxes are drawn (comment.cloud) (#4195) 2026-11-09 18:32:44 +08:00
mtlcl410 0bb77ce5e6 M5: review statuses are hidden, chain per reviewer and show per reviewer, by /IRT reference (#4122) (#4597) 2026-11-09 18:24:59 +08:00
mtlcl410 e142d30dfd M3: web and email addresses written as page text open like links (#4207) (#4472) 2026-11-09 18:12:05 +08:00
mtlcl410 c4db177d94 Merge upstream/main into fix/freetext-redact-appearance
props: a text box reports its drawn text style, other comments main's drawn_color
(grey, CMYK). Parity: main's evidence and notes with this branch's text-box and
redaction-outline sentences and tests.
2026-11-09 12:03:59 +08:00
mtlcl410 c13acf6d0f Merge upstream/main into feat/callout-reshape-1152
Keeps main's Callout font size preference with the shared callout_knee(),
main's on_text() beside the callout box resizing, and both new inspector
helpers.
2026-11-09 11:43:24 +08:00
bflatastic 02fbca9fd3 Name a kid from its /Parent as written, and draw a referenced CMYK entry as the reference gives it 2026-11-09 09:59:06 +07:00
mtlcl410andClaude Opus 5.5 cc1de556be Merge upstream/main into feat/comment-groups
Main's Replace Text (#5689) makes the caret the group's primary, as this
branch does. Kept: this branch's member-by-member deletion and release of
orphaned subordinates, with main's seed() (the strikeout also seeds its
caret) and its pair restyle; the writer returns the caret (the group's
comment) and leaves the strikeout without /Contents. Main's tests read the
strikeout from the caret's group instead of its replies, and the automation
test reads the strikeout's own colour from an XFDF export, since
comment_list gives a group its primary's attributes only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 10:57:18 +08: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
Ethunxxx fdffff7b85 M7: links that go to a page open at a chosen zoom (edit.action-goto) (#5277) 2026-11-09 10:04:00 +08:00
bflatastic b75ca7539d Merge #6504: M5: comments without an appearance read style values given by reference, and a callout without /RD is drawn (view.render-annotation-appearances)
# Conflicts:
#	crates/annot/src/draw.rs
#	parity/acrobat-features.toml
2026-11-09 08:14:50 +07:00
bflatastic 2b405cceb1 Merge #6366: M5: a text markup, line, polyline, polygon or ink comment whose points reach past its Rect is drawn through them (view.render-annotation-appearances)
# Conflicts:
#	crates/annot/src/tests.rs
#	parity/acrobat-features.toml
2026-11-09 08:12:31 +07: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
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
mtlcl410 cb3d7e2997 Merge remote-tracking branch 'upstream/main' into lane3-6504
# Conflicts:
#	crates/annot/src/draw.rs
2026-11-09 03:24:27 +08:00
mtlcl410 356e8f0ba0 Merge remote-tracking branch 'upstream/main' into float-6366
# Conflicts:
#	crates/annot/src/draw.rs
#	crates/annot/src/tests.rs
2026-11-09 03:13:30 +08:00
mtlcl410andClaude Opus 5.5 0f5de52041 Merge upstream/main into fix/freetext-redact-appearance
view.render-annotation-appearances: main now draws Thai in text boxes from the Thai face
(Type 3 glyphs beside Helvetica). A text box keeps the standard face its /DA or /DS names for
the rest of its text: lines wrap and are measured in that face with Thai shaped
(appearance::text_width_in), its font resource sits beside the Thai fonts, and after a Thai run
the text goes back to that face rather than /Helv. set_contents keeps both main's Thai refusal
and the drawn style, and fitted_box sizes the box to the Thai ink in the drawn face.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 03:09:34 +08:00
mtlcl410andClaude Opus 5.5 9f7475c97c Merge upstream/main into fix/markup-quads-outside-rect
view.render-annotation-appearances: main's appearance builder now takes the document and draws
only the usable /QuadPoints quads (resolved, malformed ones skipped). The markup reach reads the
same quads, so an indirect or partly malformed /QuadPoints that is drawn also widens the box;
the far-off-page test quads move from 1e308 (past the PDF real range, never drawn) to 1e30.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-09 02:59:30 +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
mtlcl410 b19be10a90 Merge remote-tracking branch 'upstream/main' into float-4365
# Conflicts:
#	crates/annot/src/appearance.rs
#	crates/annot/src/draw.rs
2026-11-09 02:50:44 +08:00
mtlcl410 aa45114740 Merge remote-tracking branch 'upstream/main' into lane3-6504
# Conflicts:
#	crates/annot/src/appearance.rs
#	crates/annot/src/draw.rs
#	crates/annot/src/info.rs
#	crates/annot/src/modify.rs
2026-11-09 02:29:36 +08:00
mtlcl410 cbe31d31e0 Merge remote-tracking branch 'upstream/main' into float-4094
# Conflicts:
#	crates/annot/src/info.rs
2026-11-09 02:21:54 +08:00
mtlcl410andClaude Opus 5.5 1015a51645 M5: a widened comment box takes in squiggle peaks, ink curve bulges, line captions and strokes reaching onto the page; a redraw reads only the comment's page (view.render-annotation-appearances)
- Text markup reaches past its quads by half its line width, and a squiggle by its peaks over a
  low quad; the drawing and the reach share the stroke sizes.
- Ink takes in the turning points of its smooth curve (cubic extrema), not only its points.
- A line takes in its caption's box and the caption tick.
- Points off the page whose strokes reach onto it widen the box as far as the page shows.
- page_box_listing reads the page /P names when that page lists the comment (ISO 32000-2
  §12.5.2: one page lists an annotation), inheriting its boxes up its /Parent chain, before
  walking the page tree.
- AppearanceCache entries carry the crop they were drawn for, so a changed crop box redraws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 11:44:01 +08:00
mtlcl410andClaude Opus 5.5 98f163652a M5: lines, polylines, polygons and ink whose points reach past their Rect are drawn through them (view.render-annotation-appearances)
The box a comment without an appearance is drawn in now takes in the
points it is drawn through: /L and its leader lines (ISO 32000-2
§12.5.6.7, Table 178), /Vertices (§12.5.6.9, Table 181) and /InkList
(§12.5.6.13, Table 185), as text markup already did with /QuadPoints.
Once a point lies outside /Rect, the box also takes in half the
border width, line endings and cloudy borders, up to the crop box. A
/Rect that holds every point is kept as written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
2026-11-08 06:42:51 +08:00
Josias Eli Rodriguez 65ebdab392 redact, annot: usable quad values on the f32 grid; appearances resolve indirect quads
Review round on #6525 (CodeRabbit, plus eliets2's first-pass finding):

- usable_quads returns usable values on the f32 grid, so readers folding
  f32 and f64 agree on the numbers as well as on the usable set
  (67108869 is 67108872 for every reader). New geom test pins it.
- The end-to-end redact agreement test gains the 4e38 case.
- Appearance drawing read /QuadPoints off the raw dictionary, dropping an
  indirect array or indirect entries the other readers keep.
  appearance::build now takes the document and resolves both, as redact's
  reader does; the restyle probes pass the document they already hold.
  New test pins both indirect shapes against the direct output.
- One shipped test block reformatted (rustfmt).

Drafted with AI assistance; verified by the contributor, fail-before
included.
2026-11-07 05:58:15 -06:00
Josias Eli Rodriguez 6e0d0fde3f redact: treat unusable /QuadPoints as absent in every reader
A quad counts only when all 8 entries are present, numeric and finite.
Anything else contributes nothing — never folded, shifted or zero-filled
into a box the document does not describe. With no usable quad the
existing /Rect fallback runs, and with neither the mark stays unreadable.

The rule lives in one helper, geom::quad_points::usable_quads, and redact,
render-inspect, annot summaries and annot appearance drawing all read
through it, so the outline shown is the area apply removes. Redact also
follows indirect numbers in /QuadPoints now, which it used to drop.
2026-11-07 03:04:51 -06:00
mtlcl410andClaude Opus 5.5 6a238f9fd7 M5: finding a referenced object too large to copy is charged to the budgets (view.render-annotation-appearances)
A referenced object rejected as over the per-annotation budget was walked
up to that budget and charged nothing, so many annotations (or many
references in one annotation) pointing at the same oversized nested array
each walked it again, and PASS_BUDGET did not bound the work. The walk is
now charged to the annotation's budget and through it to the pass, and a
spent budget follows no further references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 14:01:34 +08:00
mtlcl410andClaude Opus 5.5 b5e3de67fb M5: properties and edits read referenced colours as drawn; following references is bounded per display pass (view.render-annotation-appearances)
props() reads the whole annotation with references followed, so an
indirect /C or /IC (ISO 32000-2 7.3.10) shows its colour, and
"restylable" and the set_style / set_fill / set_part_opacity checks use
the same build and fallback as the display (draw::can_draw), so such a
comment can be restyled.

Following references now counts every value copied, inner arrays and
dictionaries included, and one display pass copies at most 4M values in
all, so an object many annotations point at is not copied for each of
them without bound. A cached appearance keeps what storing it needs
(the undrawable character and a note's /Rect), so a cache hit follows
no references at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 12:51:22 +08:00
mtlcl410andClaude Opus 5.5 4bbd3a2626 M5: an indirect value the builder can't draw keeps the plain drawing (view.render-annotation-appearances)
Following references can turn up a value the appearance builder leaves
alone, such as a cloudy /BE on a square. The comment then keeps the
drawing made without the references followed, instead of none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 12:01:00 +08:00
mtlcl410andClaude Opus 5.5 e69b433156 M5: comments without an appearance read style values given by reference, and a callout without /RD is drawn (view.render-annotation-appearances)
Any value may be an indirect object (ISO 32000-2 §7.3.10). The appearance
builder read only direct values, so a border style dictionary given as
`/BS 5 0 R` (Table 168) was ignored and the border drawn 1 pt solid. The
display copy, a redraw, the properties and a new width now read the
annotation with its style references followed (bounded in depth and in
copied values; links such as /P and /Popup and streams stay references).
A new width keeps an indirect /BS's other entries instead of dropping them.

/RD is optional (Table 177): a FreeText callout without it now has its
text box at /Rect and is drawn, where it was not drawn at all.

PDFBOX-4515 (annotations-2.0-without-AP).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 11:56:24 +08:00
mtlcl410andClaude Opus 5.5 e783d5106f M5: a text markup with an overflowing /Rect is not widened by its quads (view.render-annotation-appearances)
The markup test gains a highlight whose /Rect spans -1e308 to 1e308 and
whose on-page quad lies above it, so the finite-size guard in
markup_reach is what keeps its /Rect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-07 08:34:44 +08:00
mtlcl410andClaude Opus 5.5 734bff509a M5: a text markup's widened box stops at the crop box and the quad cap; the edit path saves it (view.render-annotation-appearances)
- The widened appearance box and /Rect reach the /QuadPoints only as far
  as the page's visible region (crop box within media box, ISO 32000-2
  §14.11.2), never shrink /Rect, and are not widened at all by quads
  wholly off the page or by more than MAX_MARKUP_QUADS (10,000)
  quadrilaterals (counted before the numbers are read).
- One /Rect normalisation (normalised_rect) for appearance_context and the
  reach; markup_reach takes the normalised /Rect and the page box, and
  apply_appearance takes the saved /Rect from the built /BBox
  (widened_rect) instead of computing the reach a second time.
- Tests: the restyle (set_appearance) path saves the widened /Rect, stops
  at the page and keeps a fitting /Rect as written; a markup with its own
  /AP and a non-markup subtype are not widened; crop box and cap cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 07:22:15 +08:00
mtlcl410andClaude Opus 5.5 572cec5dca A markup's quads too far apart for a width keep its /Rect; StrikeOut and Squiggly in the test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 09:11:02 +08:00
mtlcl410andClaude Opus 5.5 bf5bfbf707 M5: a text markup whose QuadPoints reach past its Rect is drawn over them (view.render-annotation-appearances)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 08:53:15 +08:00
mtlcl410andClaude Opus 5.5 1ca6842fdf Merge upstream/main into feat/comment-groups
Main added multi-selection to the comment tool (`selected` plus `also`,
`select_only`, `toggle`, `select_range`, `clear_selection`, one Delete for
all selected); this PR picks a group's member under the pointer
(`member`, `pick`, `target`). Both are kept: main's selection API resets
the picked member, `select` and `pick` go through it, a plain click or a
drag that starts a new selection picks the member, and Delete (key and
menu) takes the picked member alone while its group is the whole
selection, else every selected comment. Exports in annot lib.rs take both
sides; Comment Properties keeps main's typed-text default with this PR's
`tool_for_default` signature; other call sites take main's selection calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 03:04:53 +08:00
mtlcl410andClaude Opus 5.5 3243204e96 Merge upstream/main into feat/callout-reshape-1152
Conflicts: exports and imports (annot info.rs, engine lib.rs, the comment
tool's input.rs and paint.rs) take both sides; automation keeps main's
border-shape and opacity fields in comment_list and this PR's callout
points, and comment_edit's description has main's border colour and
opacity text with this PR's callout text box and arrow point and knee.
Main moved the end of a select gesture into `finish_select_gesture`
(multi-selection moves); this PR's callout handling (a drag on the text
box moves the box alone, a resize uses the box's frame, a reshape goes
through `reshape_for`) now lives there, and `changing_points` keeps main's
batch arm next to this PR's resize arms. In annot's tests this PR's
callout helper is `callout_box_of`, since main now imports
`modify::text_box_of` there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 02:54:26 +08:00
mtlcl410andClaude Opus 5.5 f17463a5e5 Merge upstream/main into feat/comment-popups
Conflicts in crates/annot/src/lib.rs (exports: main's border colour, border
shape and part opacity setters next to this PR's set_popup) and the comment
tool's input.rs and paint.rs (imports; a double-click now selects with
main's multi-selection `select_only` before opening the pop-up). The
pop-up connector shows for any selected comment (`is_selected`), as main
keeps several selected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 02:30:46 +08:00
mtlcl410andClaude Opus 5.5 39d09ff25d Merge upstream/main into feat/dynamic-stamps
Conflict in crates/engine/src/edit/run.rs: main added the PasteComments
arm next to AddCustomStamp; both are kept, AddCustomStamp with this PR's
text line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 02:14:59 +08:00
mtlcl410andClaude Opus 5.5 8c70cb730b Merge upstream/main into fix/freetext-redact-appearance
Conflicts in crates/annot (appearance.rs, info.rs, modify.rs): main added a
text box border colour of its own (`RG` in /DA, `da_border`), oval and
circle borders, per-part opacity and snug circles; this PR draws and
restyles the text in the face /DA, /DR and /DS give (`text_style`).
Both are kept: the text is laid out with the face's widths and centred in
an oval; restyling (colour, size, and now set_border_color) keeps the face,
the font resource and the border colour in /DA (`restyle_text_bordered`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyDBXygTf7jcqdsjcfn
2026-11-06 01:30:42 +08: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
0839c1e761 Fill & Sign: a Border control for typed text, and a border colour of its own (#4146)
* 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>

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 17:08:08 +01:00
42bc4f117d M5: copy and paste comments between documents, select several, finish connected lines from the menu (#3484)
Comments copy and paste, within and between documents. ⌘C with comments selected (and no text
selected) copies them, each with its replies, as XFDF: into the app's clipboard (every tab and
window) and as text onto the system clipboard. ⌘V, Edit ▸ Paste comments or the page's context
menu pastes them onto the current page at their own positions, as new comments under names of
their own, so nothing is replaced and pasting twice gives two sets; the copies are selected.
XFDF with comments from the system clipboard pastes too, so comments travel between processes.

pdfcraft_xfdf::export_xfdf_comments writes a selection with its replies; import_comments takes
a target page and a name prefix and imports copies (the usual import still replaces same names).
Engine: Edit::PasteComments { xfdf, page } (one undo step, comment permission) and
Session::export_comments. Tools: comments_copy (read-only) and comments_paste.

Several comments select at once: ⌘-click (Ctrl on Windows and Linux) or ⇧-click adds one or
takes it out, on the page and in the Comments panel; the one clicked last has the handles.
Delete removes them all as one step, a drag moves them together, the context menu copies them.

Connected lines, polygons and clouds finish from the right-click menu's Complete (with Undo last
point and Cancel drawing), as other editors do, besides Enter and a double-click.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 16:41:45 +01:00
c3c36f77cf M5: a comment's text, border or line, and fill each at an opacity of its own (#4542)
* A comment's text, border or line, and fill each at an opacity of its own

Comment Properties showed one opacity for a whole comment, so a text box's
see-through background took its text and border with it, and a rectangle
marking an area on a plan couldn't have an opaque line over a faint fill.
Now a text box, typewriter text or callout has a Text, Border and Fill
opacity, a rectangle, oval, polygon or cloud a Line and Fill opacity (a part
not drawn is greyed); everything else keeps one Opacity.

The line's stays /CA, the fill's is /ca (PDF 2.0's opacity for what is
filled) and the text's /PCTextOpacity, each only where it differs from the
one it would follow, and the appearance draws them with /GS0 (strokes at the
line's, fills at the fill's) and /GS1 for the text, so every viewer shows
them. The quick bar's Opacity and StyleAnnotation's set the whole comment's.

Engine: Style::{fill_opacity, text_opacity}, Edit::PartOpacity,
annot::{set_part_opacity, appearance::part_opacity}, Props::{fill_opacity,
text_opacity}. Make Properties Default and the settings carry the parts.
Headless: comment_add / comment_edit fill_opacity, text_opacity; comment_list
reports opacity, fill_opacity, text_opacity. Four labels in every catalog
(AI-assisted translations from English, noted in ATTRIBUTION.toml).

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

* Review: the text's opacity slider shows what the text is drawn at; comment_list skips replies' properties; ui-parity's change record

A text box without fill and border whose text kept an opacity of its own showed the border's
in its Text opacity slider; it now shows the text's, and a change sets the whole comment's.
The opacity sliders are named by their labels. comment_list reads a comment's properties only
for comments, not for every reply.

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

* Rebase: comment_list tells replies by is_reply, as main now threads them

main threads replies by their /IRT reference (#4061); the opacity fields skip a reply the
same way.

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

* Review: a text-only text box made default gives the tool its text's opacity

With neither a border nor a fill, a text box's text is the whole comment:
Comment Properties shows its drawn opacity (the text's own, when set), and a
change there sets the whole comment's. Make Properties Default, and the
context menu's Make Current Properties Default, stored /CA instead, so a
text box whose text was at 50 % gave the tool 100 %. The default is now the
text's drawn opacity in that case.

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

* Rebase: the part opacities in main's annot layout (Style's fields, the text box drawing's parts, set_part_opacity exported)

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

* Rebase: main's new comment test builds a Style with the part opacities

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

---------

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