Commit Graph
115 Commits
Author SHA1 Message Date
KenM76andClaude Opus 5.5 ace92f3f2f Merge origin/main
Tests only: both sides added tests at the same place; both kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-03 11:09:49 -05:00
AdrianTM 0d3f57e120 M5: keep Fill & Sign editing on Enter and preserve line breaks (#5850) (#5954)
* M5: keep Fill & Sign editing on Enter and preserve line breaks (#5850)

* M5: preserve typewriter whitespace through readers and edits (#5850)

* M5: retain direct-subtype comment listing for Fill & Sign fix (#5850)
2026-11-03 10:24:45 -05:00
KenM76andClaude Opus 5.5 4c087336aa M12: 3D models: declare PRC's Adobe extension before 2.0; refuse a poster with no area
Before PDF 2.0, PRC in a 3D stream is an Adobe extension to PDF 1.7, so a
PRC model added to a file below 2.0 now declares /Extensions /ADBE at
BaseVersion 1.7, ExtensionLevel 1, keeping other extensions and a later
ADBE level, and leaving an /ADBE that is not a dictionary (an array of
them) alone (ISO 32000-2 §7.12). U3D and 2.0 files are unchanged.

A PDF poster whose page box has no area was drawn as nothing behind a
size forced to 1; it is now refused with EditError::Source.

Both found by CodeRabbit. Tests a_prc_model_declares_the_adobe_extension_before_pdf_2
and a_3d_poster_page_without_area_is_refused fail with their fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-02 18:48:12 -05:00
KenM76andClaude Opus 5.5 9da253ebee fix(annot): a 3D poster fitted to its rectangle with rounding is taken
A poster box fitted to the annotation rectangle in floating point can
end a few ulps outside it (a 7x3 picture centred in 0 100 3.6 110.8
spans -2.2e-16 to 3.6000000000000005), and the exact containment check
refused it. The check now allows a slack of 1e-9 of the rectangle's
largest coordinate, clamps the box to the rectangle, and only then
requires it to have width and height, so a box wholly within the slack
outside the rectangle is still refused.

Tests: a_3d_poster_fitted_to_its_rectangle_with_rounding_is_taken
fails without the slack (Invalid); the new case in
a_3d_poster_outside_its_rectangle_or_without_a_size_is_refused fails
when the size check runs before the clamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-02 17:24:42 -05:00
KenM76andClaude Opus 5.5 16ffab9b03 M12: add a 3D model (U3D or PRC) to a page with a poster (#1297)
A new `model_add` automation tool and `Edit::AddModel3d` embed a U3D or
PRC file as a 3D annotation (ISO 32000-2 13.6.2) in a chosen rectangle:

- The model goes into a Flate-compressed 3D stream (13.6.3) with /Type /3D
  and /Subtype U3D or PRC, read from the file's header (`U3D` and a zero
  byte, or `PRC`); anything else is refused before the document changes.
  Models over 256 MiB (MAX_MODEL_BYTES) are refused. model_add checks the
  file's size (from the file system) and its header before reading the
  rest, so a large file that is not a model is never loaded; the path goes
  through the read root, and `read_model` joins the helpers the
  file-access test knows take a resolved path.
- /3DD references the stream, so the annotation has its own instance.
- /3DA states /A: click (XA, the standard's default and the tool's),
  page_open (PO) or page_visible (PV).
- /AP /N is the poster readers print and show before activation: a chosen
  image or PDF page fitted inside the box keeping its shape and centred (the
  custom-stamp picture path: `mark_source`, `fit_picture`, and the stamp's
  placement matrix now shared as `appearance::picture_place`), or, with no
  picture, a light frame labelled "3D model". It is drawn in user space and
  not counter-rotated, so on a page with /Rotate it turns with the page, as
  the model does.
- /Contents carries the name (the file stem by default) as the alternate
  description (12.5.2).
- A document older than the model needs gets /Version in its catalog
  (7.7.2): 1.6 for U3D, 1.7 for PRC, as the optimizer does for JBIG2.
  Adobe's extension level for PRC in 1.7 is not written.
- The 3D rectangle has its own MAX_MODEL_SIDE (14400 pt).

The edit is undoable ("Add 3D model"), needs the comments permission and
rebuilds only the comments view. A new parity entry, misc.3d-add, is
partial: no poster rendered from the model, no views (/VA, /3DV), no
replacing the poster, no UI command, no /Extensions. misc.3d-view is
unchanged. ROADMAP.md progress log line.

Adapted from pdfcer (MIT, same author), commit 1ca5ece, rewritten to this
repository's types and rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-02 16:09:08 -05:00
archamon faebcfd264 fix(annot,ui): stabilize ink width bounds, search translated tool labels, and fix hotbar settings label 2026-11-01 20:58:16 +00:00
archamon 53c79ca9b0 feat(ui,annot): customizable hotbar strip, freehand highlight, and moveable navigation rail
- Add Freehand-Highlight tool with 100pt line width support, Lucide highlighter icon, and PDF Multiply blending.
- Add customizable floating and docked hotbar with command picker, size slider, full-side strip chrome integration, and settings gear icon anchored at the bar end.
- Add left/right docking options for the navigation rail with toggle button and preferences setting.
2026-11-01 20:23:12 +00:00
FredApps b21af3b969 Integrate 21 clean-merging PdfCraft parity PRs
Preserve each original PR head and its authorship as a merge parent.
Includes #4304 #4335 #4384 #4404 #4179 #5300 #4719 #4541 #2815 #4620 #3789 #3779 #2960 #5208 #4596 #3636 #4494 #4013 #5134 #2133 #2635.
Conflicting candidates skipped. Combined CI pending; see docs/integration-20261030.md.
2026-10-30 10:35:42 +01:00
Oliver HagmannandClaude Opus 5.5 f0cd96e90b Review: isolate_annotations says that an index off the page's list is an error
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:54:15 +01:00
Oliver HagmannandClaude Opus 5.5 b68362bdc2 Rebase: isolate_annotations is exported from main's lookup module
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:50:58 +01:00
Oliver HagmannandClaude Opus 5.5 67b5b2dcf6 A moved comment is drawn where it goes while it is dragged
Moving a rectangle, an oval or any other comment showed only its blue
frame going, the shape staying where it was until release. The comment is
now rendered alone once as the drag starts (Document::comment_layer: a
read-only copy with only that comment, no page content) and laid over the
page where it goes; the page beneath keeps showing it where it was until
the move is applied, as a CAD program's move does. Rendering the page
without it would cost a whole page's render at every drag on a heavy plan.
Stamps and Fill & Sign marks keep their own layers. annot: isolate_annotations
for several comments of a page, so a group can be drawn together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 05:50:58 +01:00
alehelandClaude Opus 5.5 6a1783599d fix(comments): Delete asks the engine whether it would delete
The menus greyed Delete out from the listing's reply links while the
engine refused on the PDF's own /IRT and /Parent links, so the two could
disagree. pdfcraft_annot::deletable is now the one check: delete_annotation
runs it, and Document::annotation_deletable gives the Comments panel and
the page's context menu the same answer before they offer Delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:09:59 +01:00
alehelandClaude Opus 5.5 28a31aebc0 fix(comments): previews lift a stamp whatever its replies' locks; Delete greys out over a locked reply
The locked-reply check in delete_annotation also reached stamp_preview
and signature_image, which only take the annotation off a copy to draw
the page beneath it: an unlocked stamp with a locked reply lost its drag
preview. They now use lift_annotation, which checks no locks.

The Comments panel's Delete (comment and reply menus) and the page's
context menu are greyed out while a reply below is locked
(comments::locked_below), rather than refusing after the click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:33:14 +01:00
alehelandClaude Opus 5.5 72edd945e5 fix(comments): a locked reply keeps the comment or reply it answers
Deleting a comment or a reply took every reply to it, locked or not: only
the target's own Locked flag was checked. A locked reply can't be deleted
on its own, so delete_annotation now refuses while anything that would go
with the target is locked (pop-ups follow their comment).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:14:42 +01:00
Dylan M. Taylor 81c63c037a Group annotation, form and document operation parameters 2026-10-27 23:25:39 +00:00
Dylan M. Taylor 7d1ed99f51 Refactor annot for strict Clippy checks 2026-10-27 23:12:50 +00:00
aiongg 2e8d361123 refactor(annot): split appearance.rs into appearance/
New module: symbols.
2026-10-26 22:18:50 +00:00
aiongg 5ce4d24412 refactor(annot): split lib.rs into src/
New modules: types, lookup, create, modify, draw, info.
2026-10-26 22:18:50 +00:00
aiongg e0c319d273 Integration: a comment listed through a reference chain keeps its replies after an edit (#4061) 2026-10-24 23:18:59 +00:00
aiongg d821f91001 Integration: fix(comments): thread replies by their /IRT reference, count comments like Acrobat (#4061) 2026-10-24 22:48:11 +00:00
aiongg f94b34adc9 Integration: fix(redact,cos): cleared redaction marks leave nothing behind (#4226) 2026-10-24 22:48:11 +00:00
alehel ad8c2f46dd Merge origin/main into fix/comment-threads-by-irt
Conflicts:
- crates/render/src/inspect.rs: this branch's reply-linking methods and main's NoRotate
  rectangles (#3016) both go in the Annotation impl; the annotation reader takes this
  branch's loop with main's no_rotate field.
- crates/automation/src/comments.rs: this branch's HashMap import, main's import list.
- ATTRIBUTION.toml: each catalogue's source note keeps main's secured-document clause and
  this branch's reply-count clause; ATTRIBUTION.md regenerated (cargo xtask assets --write).
- The 33 catalogues: main's new rows, then this branch's reply-count row.

main's #3016 tests build Annotation field by field; they now set parent and grouped.
2026-10-24 20:42:11 +02:00
mtlcl410andClaude Opus 5.5 4f436d3dd4 M5: line comments draw their leader lines and caption (ISO 32000-2 Table 178) (#4354)
A Line annotation without an appearance was drawn along /L with its
endings there, ignoring /LL, /LLE, /LLO, /Cap, /CP and /CO. With /LL,
/L gives the ends of the leader lines, not of the line itself.

- The line sits |LL| + LLO from /L, on the left of travel for a
  positive /LL; leader lines run from LLO off /L to LLE past the line.
- /LE endings are drawn at the line itself.
- /Cap true draws /Contents (or the plain text of /RC) in black
  Helvetica 10, turned with the line: Inline breaks the stroke around
  it, Top sets it above, /CO shifts it (with a tick when moved off).
- PdfCraft measurements keep /PCMeasureValue as their caption.
- undrawable_text reports caption text the standard font can't draw.
- Non-finite, huge and negative values, and a zero-length /L, are
  handled leniently; captions are cut to 256 characters.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:30:16 -04:00
alehel c16956f539 Merge origin/main into fix/comment-threads-by-irt
main's #4284 stopped hashing translation catalogues in ATTRIBUTION.toml; the
conflict took main's side (no sha256 lines) and kept this branch's source notes.
2026-10-24 11:31:20 +02:00
aiongg a7778f92f1 Merge PR #4362: M5: comments without an appearance draw their /Border dashes, Ink dashes and the caret's ¶ (ISO 32000-2 Tables 166, 168, 183) 2026-10-24 06:23:19 +00:00
aiongg 581faa876a Merge PR #4349: M5: stamps without an appearance draw the stamp they name (ISO 32000-2 Table 184) 2026-10-24 06:23:15 +00:00
aiongg ee989ab5ca Merge PR #3016: M5: sticky notes stay upright where they are placed on rotated pages (NoRotate, #2583)
Conflicts:
- parity/acrobat-features.toml: comment.note: kept main's icon notes text, added the NoRotate sentence and the PR's three evidence entries.
- vendor/README.md: kept main's rows; added the PR's hayro-interpret patch as (53) with its test (53).
2026-10-24 06:22:25 +00:00
a047c6ba09 chore: resolve SonarCloud maintainability issues (#4356)
* chore: resolve SonarCloud maintainability issues

* fix(packaging): use verify_sha256 helper function

* style: cargo fmt

* fix: restore wildcard imports and fix method-reference type errors

Revert the glob-import expansions (they missed SomIndices and left unused
imports), revert char::is_ascii_digit where the iterator yields char by
value, and pass the content slice to prune_doomed_xobjects as &[Vec<u8>].

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

* Keep XObject decodability check without binding content in interp

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
2026-10-23 23:28:18 -04:00
mtlcl410andClaude Opus 5.5 001a900ac7 M5: comments without an appearance draw their /Border dashes, Ink dashes and the caret's ¶ (ISO 32000-2 Tables 166, 168, 183)
Comments that arrive without /AP are drawn from their dictionaries by
appearance::build, which lost three entries:

- /Border [hr vr w [dash]] (Table 166): the dash array made the whole
  array unreadable, so the width fell back to 1 and the border was solid.
  The width is now element 2 whatever follows, and without /BS the
  /Border dash array is drawn. /BS, when present, wins for both (Table
  166: "the Border entry is ignored"), so a /BS without /W means 1.
- Dash arrays are drawn only when finite, non-negative, not all zero
  (§8.4.3.6) and at most 64 long; anything else draws solid.
- Ink applies its /BS dash pattern (Table 168, Table 182), like the
  other stroked shapes.
- A caret with /Sy /P (Table 183) shows a ¶ (Helvetica, WinAnsi 0xB6) to
  its right in its colour, inside /Rect: in the room /RD leaves, or by
  narrowing the caret. /Sy /None draws as before.

The Helvetica font resource, written out three times, is one helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 10:41:46 +08:00
mtlcl410andClaude Opus 5.5 2f97fd2d59 M5: the placeholder for an unknown stamp name stays grey whatever its /C
An AP-less stamp whose /Name has no drawing got its placeholder frame in
the annotation's /C colour (/C [1 0 0] drew a red frame), while the doc
comment and parity note promise a neutral grey one. The frame now always
strokes PLACEHOLDER_GREY; the fourteen Table 184 names keep /C.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 09:27:38 +08:00
mtlcl410andClaude Opus 5.5 3db28cc4e3 M5: stamps without an appearance draw the stamp they name (ISO 32000-2 Table 184)
A rubber stamp from another program with no normal appearance was not
drawn at all. The display copy now gives it one:

- the fourteen standard names of Table 184 draw that stamp with
  PdfCraft's stamp drawing (eight from the palette, six with labels of
  their own), fitted to /Rect, in /C or the stamp's colour;
- no /Name draws Draft, the table's default, unless /IT says the stamp
  is an image or a snapshot;
- any other name (names match byte for byte, §7.3.5) draws a thin grey
  frame with both diagonals, so the stamp stays visible and selectable.

The drawing is display-only, like the other comments without an
appearance: Save writes the file as it was, and build() still declines
these stamps, so restyling and flattening never write it into the file.
A stamp with its own /AP /N is never touched. stamp() now takes the
label and the sign-here flag instead of a StampKind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 09:15:12 +08:00
alehelandClaude Opus 5.5 195a2c9bab fix(annot,redact): inline replies and pop-ups go with what they point at
`/Annots` may hold an annotation dictionary inline instead of a reference
(the standard asks for references, but files do it and PdfCraft accepts
them). Deleting a comment or clearing redaction marks only matched
referenced entries, so an inline reply (`/IRT`) or pop-up (`/Parent`) of
what went stayed behind, pointing at nothing. The pop-up an inline reply
or an inline comment names (`/Popup`) stayed too. This predates the shared
walk.

`goes_with` matches an entry against what is going, inline entries
included; `delete_annotation` and `clear_marks` filter with it. The walk
follows the pop-up an inline reply names, and deleting an inline comment
takes the pop-up it names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:54:53 +02:00
alehelandClaude Opus 5.5 d9bc4b17a5 fix(redact): clearing marks leaves the page as it was and takes replies with them
Clear all wrote `/Annots []` on a page that had no annotations and replaced
an indirect `/Annots` array with an inline copy. It also left replies to a
mark in place, so their `/IRT` kept the cleared mark in the document (and in
the comment list, as a reply to nothing).

`clear_marks` now writes the list through the annot crate's `set_annots`
(made public), as deleting a comment does, and removes each mark's replies
and pop-ups with it. The "what goes with a deleted comment" walk moves from
`delete_annotation` into `with_replies_and_popups`, which both use; it visits
each annotation once instead of sweeping the list until nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:54:52 +02:00
alehelandClaude Opus 5.5 21814f60f5 fix(comments): show stray and nested replies, leave Replace Text out of the count
Follow-up to threading by /IRT reference, from review:

- An /IRT that reaches no listed comment (the annotation itself, a
  loop, a pop-up, an object no page lists) left the annotation a reply
  shown nowhere, though still counted. `link_replies` now settles the
  links in both readers (inspect on open, the engine's list after an
  edit): a reply keeps its parent only when the chain reaches a comment
  within 64 steps, and anything else stays a comment of its own.
- A reply to a reply was never shown in the Comments panel (only
  comment_list followed the chain, #806), but the new header line
  counted it. The panel now files every text reply under the comment
  its thread starts from (`thread_tops`); a status or checkmark stays
  with the annotation it was set on.
- A Replace Text caret (/RT /Group) is grouped with its strikeout, not
  a reply to it, so the header no longer counts it (new `grouped`
  field on both readers).
- The engine summary and comment_list gather replies once instead of
  rescanning every annotation per reply (quadratic on large reviews).
- Inspect takes the first listing in page order instead of a min over
  an unordered map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:30:08 +02:00
Claude be600950c9 fix(comments): thread comment replies by /IRT reference, count comments like Acrobat
Replies were matched to their parent by /NM across the whole document.
/NM need not be unique: PyMuPDF names the first comment on every page
`fitz-A0`, so every reply in such a file was filed under page 1's
comment, and the other pages showed none. Both comment readers (render
inspect on open, annot summaries after an edit) now resolve /IRT to the
parent's page and /Annots position by object reference, and the
Comments panel, comment properties, the canvas thread, the engine's
comment summary and the comment_list tool thread by that.

The Comments header now counts comments without their replies, as
Acrobat does (28 rather than 44 for the reported file), and a reply's
thread line runs from the avatar above it (the comment's or the
previous reply's) to its own instead of a 12 px stub.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KvugvPrjZtPVudhh7cMa54
2026-10-23 17:30:07 +02:00
mtlcl410andClaude Opus 5.5 20a395c0ae M5: sticky notes draw the icon they name (ISO 32000-2 Table 175) (#4182)
A note's /Name picks its icon, and ISO 32000-2 §12.5.6.4, Table 175 asks
every viewer to provide icons for seven standard names: Comment, Key,
Note, Help, NewParagraph, Paragraph, Insert ("Additional names may be
supported as well"). PdfCraft drew two shapes: Comment, and a page for
everything else. So choosing Key, Help, Insert, NewParagraph or
Paragraph in Properties changed nothing visible, and notes in received
files that use these names, or further ones such as Check or Star, all
looked like the same page. Recolouring such a note redrew it as a page.

note_icon now draws a shape of its own for each standard name and for
seven further names in common use (Check, Circle, Cross, CrossHairs,
Star, RightArrow, UpArrow). They are simple geometric drawings written
for PdfCraft, filled in the note's colour with a dark outline like the
existing two. NoteIcon gains the further names (NoteIcon::ALL); the
Properties chooser and comment_add offer all fourteen. Another name
still draws as Note, the table's default.

Tests: every_note_icon_draws_its_own_shape (all fourteen differ; an
unknown name and a missing one draw Note) and
a_star_note_keeps_its_star_when_recoloured. Mutants, each failing them:
from_name limited to the standard seven, Star or Key drawn as Note, an
unknown name drawn as Comment. One survivor reshaped Circle while
keeping it distinct, which the distinctness test rightly allows.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:42:26 -04:00
Guillem Hermida 558fa0c995 Merge remote-tracking branch 'origin/main' into fix/2583-note-icon-no-rotate
# Conflicts:
#	ROADMAP.md
#	crates/annot/src/lib.rs
#	crates/automation/src/comments.rs
#	crates/engine/src/lib.rs
#	crates/engine/src/tests.rs
#	crates/render/src/inspect.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/comments_panel.rs
#	crates/ui-egui/tests/it/comments.rs
#	parity/acrobat-features.toml
#	vendor/README.md
2026-10-23 10:19:47 +02:00
Dylan M. Taylor 93b25f67a6 Merge main into fix/coderabbit-c5-forms-misc
Keep the Ops iterator from this branch and carry main's long-number skip
(MAX_RETRIED_TOKEN) into its operand error path.
2026-10-22 22:07:19 -04:00
aiongg 6394779b81 fix(annot): check a whole link batch before creating any link object 2026-10-22 20:51:55 +00:00
aiongg 02f0f7396f fix(annot): count web-address text after its UTF-8 conversion
An address of invalid UTF-8 grows up to three times when converted, so
the listing's 16 MiB cap counted fewer bytes than it kept. The cap now
counts the converted text; the raw length is still checked first, so an
address already over the cap is never converted.
2026-10-22 20:04:04 +00:00
aiongg c2e96de0b2 fix(annot): extend each page's annotation list once when adding many links
add_many added links one at a time, and each addition walked the page tree
and copied and rewrote the page's whole /Annots array, so a batch of links
cost quadratic time. The pages are found once and each page's new links are
appended in one update.
2026-10-22 18:02:33 +00:00
aiongg 05d09de1e3 fix(annot): read a shared page tree Kids array once
The page walk pushed a node's /Kids array for every new node, so many page
tree nodes sharing one indirect /Kids array re-read it for each node:
quadratic work while the seen check skipped every entry. A shared array
object is read under the first node that lists it, and the walk stops
after four million /Kids entries.
2026-10-22 18:02:33 +00:00
aiongg 1695106f98 fix(annot): list a repeated link once and cap its web-address text
The link listing copied the action's URI into a new String for every
/Annots entry, so one annotation referenced many times, or many small
links sharing one long URI string, multiplied that string in memory.
The listing skips annotation references it has already listed and keeps
at most 16 MiB of web-address text; a link past that lists as an
uneditable action, which leaves it unchanged in the file.
2026-10-22 17:08:54 +00:00
aiongg e07942c829 fix(annot): cap the bumps of a cloudy outline
The cloud border clamped bumps per edge only, so a polygon with many long
edges asked for 10,000 cubic segments on each one. The bump radius now
grows so the whole outline draws at most 20,000 bumps plus one per edge.
2026-10-22 17:08:54 +00:00
aiongg c00a8b7616 Merge r2 track t14: signatures
# Conflicts:
#	ATTRIBUTION.toml
#	crates/annot/src/tests.rs
2026-10-22 02:22:02 +00:00
aiongg 8bb2eef740 Merge r2 track t13: cos-parse, vendored-crate fixes
# Conflicts:
#	ATTRIBUTION.md
#	crates/annot/src/tests.rs
#	crates/forms/src/tests.rs
#	crates/organize/src/tests.rs
2026-10-22 02:21:53 +00:00
aiongg 5a13228b57 Merge PR #3520: M5: a stamp grows and shrinks with its handles before release (#3518) 2026-10-22 01:46:28 +00:00
aiongg b57f3c1a76 Merge PR #3816: fix(annot): a comment listed many times gets one display appearance 2026-10-22 01:46:19 +00:00
aiongg 0b1cce39bc Merge PR #3781: Fill & Sign marks stay upright on rotated pages (#2392) 2026-10-22 01:46:17 +00:00
aiongg 145a08ae96 Merge PR #3788: M5: drag a line's ends (and a polygon's corners) to change its length and angle
ROADMAP.md: union of progress-log lines.
crates/ui-egui/src/i18n/*.tsv (20 catalogs): union of appended keys.
2026-10-22 01:45:21 +00:00