Commit Graph
3468 Commits
Author SHA1 Message Date
ihsan mokhlisse 8a78fb21cf M3: translate the menu bar's own overflow sentinel to a glyph every font has
craft_menu writes "⋯" (U+22EF) itself for the menu of titles that don't fit, through the app's own label callback. No font this app ships or falls back to has that glyph, and egui 0.37's MissingGlyphPolicy::Panic crashes on it as soon as the bar is narrow enough to need it: every editing test that narrows the window while a paragraph is open hit this. The ordinary ellipsis the rest of the app already uses everywhere else is available and reads the same.
2026-10-29 10:13:01 +01:00
ihsan mokhlisse dbeb9fa963 M7: move a paragraph instead of rewriting it to trigger a stale-number regroup
wrap()'s own test for mixed exotic whitespace fed through it directly, bypassing the caller's CR/CRLF normalization, needed its expected value updated for #4199's keep-every-space design, landed on main after this branch's base.
2026-10-29 10:11:19 +01:00
ihsan mokhlisse b282ec26be M7: check a shared text object through the tools (#2517)
Editing either end of one text object, then saving and editing again, has to leave the other paragraph in place. The library tests did not drive text_edit and doc_save.
2026-10-29 10:11:19 +01:00
ihsan mokhlisse 59513aa887 M7: keep a preceding paragraph out of a shared text object's marker (#2517)
A partial rewrite of the last paragraph opened the marker at the shared BT, so the paragraph before it was inside the region.
2026-10-29 10:11:19 +01:00
ihsan mokhlisse 62af4f87ca M7: compile the paragraph fixture on current main (#2517)
The paragraph-key helper has to carry reflow text, which a width change uses. ClosePath on an empty subpath is a match guard so the edit crate still passes clippy.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 21b911ae42 M7: close a shared text object before the next paragraph (#2517)
A partial rewrite put the paragraph marker around the shared ET, so the neighbour in that text object joined the paragraph and the next edit deleted it.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse cb832b0206 M7: close a partial rewrite before the next marked region (#2517)
An unmarked paragraph was closing its marker at the following line's BT, which is after that line's BDC. The close now follows this paragraph's last ET.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 2bc2933988 M7: rejoin a wrapped line when the box is widened (#2517)
A newline that was typed stays. A wrap is marked so a later width change can reflow it, and a short unmarked line stays broken.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse a84ae042d1 M7: close a partial rewrite's marker after the paragraph (#2517)
A row left in the source stays in the last paragraph. An extra marker is not placed across two marked-content regions.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 4ebb575a88 M7: keep a repeated step without TL, and a partial rewrite's neighbour (#2517)
Three or more rows at one repeated step stay one paragraph when the file has no TL, which is how LibreOffice writes each line. A rewrite that leaves a source row still marks the whole paragraph, so the neighbour survives a second edit. Dragging the box width keeps the line breaks.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse 53e6d832af M7: keep an explicit leading, and a grown paragraph's neighbour (#2517)
The join limit follows the text object's TL when it has one, checked against the measured baseline gap. A rewrite of every row is marked so a new row one leading above the next paragraph does not absorb it.
2026-10-29 10:11:18 +01:00
ihsan mokhlisse e228e0edfc M7: keep a blank line as a paragraph break (#2517)
A gap of 2.4 times the font size was still under the 2.5 times join limit, so two paragraphs became one and editing the first deleted the second. Once a paragraph has two rows, a wider gap than its own leading stays the next paragraph.
2026-10-29 10:09:18 +01:00
aiongg 3109bb2359 Merge pull request #5236 from FredApps/integration/kenm76-2026-10-29
Integrate 13 KenM76 fixes for forms, text and rendering
2026-10-28 22:32:00 -04:00
FredApps d99538145c Clarify integrated vendor regression coverage 2026-10-29 02:43:29 +01:00
FredApps 97c062a14b Complete additive integration fields and test boundaries 2026-10-29 02:40:41 +01:00
FredApps f280f3a282 Integrate KenM76 PR #5228
# Conflicts:
#	vendor/README.md
#	vendor/hayro-interpret/src/device.rs
2026-10-29 02:39:15 +01:00
FredApps 7fc2033421 Integrate KenM76 PR #5199
# Conflicts:
#	crates/render/tests/it/main.rs
#	vendor/README.md
2026-10-29 02:38:56 +01:00
FredApps 1b0e3b6110 Integrate KenM76 PR #5178
# Conflicts:
#	vendor/README.md
2026-10-29 02:38:44 +01:00
FredApps e34cb1d1cc Integrate KenM76 PR #4991
# Conflicts:
#	crates/automation/tests/it/automation.rs
#	crates/forms/src/author/edit.rs
#	crates/forms/src/tests.rs
#	crates/forms/src/values.rs
#	parity/acrobat-features.toml
2026-10-29 02:38:25 +01:00
FredApps 07edd05aea Integrate KenM76 PR #4987
# Conflicts:
#	crates/forms/src/author/edit.rs
#	crates/forms/src/tests.rs
2026-10-29 02:38:03 +01:00
FredApps 9e30913d6e Integrate KenM76 PR #4977 2026-10-29 02:36:20 +01:00
FredApps d1fcc12e07 Integrate KenM76 PR #4943 2026-10-29 02:36:20 +01:00
FredApps ebd7578cc3 Integrate KenM76 PR #4913
# Conflicts:
#	crates/render/tests/it/main.rs
2026-10-29 02:36:20 +01:00
FredApps d9ddd8a8b0 Integrate KenM76 PR #4906 2026-10-29 02:35:58 +01:00
FredApps b0555cb918 Integrate KenM76 PR #4898
# Conflicts:
#	vendor/README.md
2026-10-29 02:35:58 +01:00
FredApps a66686cb0d Integrate KenM76 PR #4862 2026-10-29 02:34:57 +01:00
FredApps f716bd1462 Integrate KenM76 PR #4841
# Conflicts:
#	crates/automation/tests/it/main.rs
2026-10-29 02:34:57 +01:00
FredApps 2e6f7e5d3f Integrate KenM76 PR #4831 2026-10-29 02:34:41 +01:00
KenM76andClaude Opus 5.5 367f7d5432 fix(render): a pattern before an isolated CMYK group no longer moves it
From the review of #5228. The finished CMYK group was drawn through
whatever paint transform a preceding shading pattern left on the
context, so its cyan half showed magenta. Hayro patch (27) now resets
the paint transform before drawing the group's pixels.

Two regression tests, both reported in that review:
- a pattern painted before the group (fails without the reset);
- the Color blend mode over an empty CMYK group, swept over all 256 K
  levels (failed on nine levels before #5199's vello_cpu fix was merged
  in; passes with it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 20:40:16 -04:00
KenM76 e518e27778 Merge #5199 (fix/color-blend-gray-black) so the CMYK K ink pass blends gray with Color correctly 2026-10-28 20:37:34 -04:00
KenM76andClaude Opus 5.5 3aeafeb09e Merge origin/main into fix/cleared-default-stays-cleared
Resolves test-file conflicts where main and this branch both appended tests:
main's file plus this branch's appended tests, unchanged.
fmt, clippy -p pdfcraft-forms -p pdfcraft-automation, and both crates' tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 19:18:37 -04:00
KenM76andClaude Opus 5.5 304f27fd0b Merge origin/main into fix/choice-index-follows-options
Resolves test-file conflicts where main and this branch both appended tests:
main's file plus this branch's appended tests, unchanged.
fmt, clippy -p pdfcraft-forms -p pdfcraft-automation, and both crates' tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 19:18:35 -04:00
KenM76andClaude Opus 5.5 2a26479cbd Merge origin/main into fix/radio-join-group-by-name
Resolves test-file conflicts where main and this branch both appended tests:
main's file plus this branch's appended tests, unchanged.
fmt, clippy -p pdfcraft-forms -p pdfcraft-automation, and both crates' tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 19:18:32 -04:00
KenM76andClaude Opus 5.5 6fcfb3f98d fix(render): an isolated CMYK group inside a knockout element is erased, not painted
main's knockout patch (hayro (22)) draws each element of a knockout group
once in erase mode, to clear what the group drew under the element's shape.
An isolated DeviceCMYK group nested in such an element still took the CMYK
path in that pass, and drew its finished pixels with Normal blending over
what should have been cleared. draw_isolated_cmyk_group now declines while
erasing, so the group's contents are interpreted in erase mode as before.

The new test fails without the fix (the centre shows red through the
magenta: [240, 63, 168] against [245, 127, 197] for the DeviceRGB control).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 18:20:29 -04:00
KenM76andClaude Opus 5.5 41277846a3 fix(vendor): keep draw_isolated_cmyk_group's doc comment on that function
The nested-group hook was inserted between the comment and the function it
documents, so rustdoc gave it to group_blends_outside_device_cmyk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 18:20:28 -04:00
KenM76andClaude Opus 5.5 d1f5658c89 chore(vendor): renumber the CMYK-group patches after rebasing onto main
main has taken hayro-interpret patches up to (66) and hayro up to (25)
since #3777 was opened, and open PRs claim hayro-interpret (67)-(69) and
hayro (26). The isolated DeviceCMYK group patches become hayro-interpret
(70) and hayro (27). vendor/README.md names the tests by their new path,
raster::tests::isolated_cmyk_groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 18:20:28 -04:00
jpeskovandClaude Opus 5.5 f4fe2d9210 fix(render): blend large CMYK groups in every tile; decline nested isolated other-space groups
Review of #3777 (test report by mtlcl410).

- A group wider than about 512 device pixels lost its blend mode in the
  tiles that a paint covers completely: on the bare pass canvas, vello_cpu
  fills such a tile without the blend mode. At 300 dpi, magenta then black
  with Difference drew black in the middle of the group, not green. Each
  ink pass now draws inside a layer of its own.
- A nested isolated group that names another blending space (for example
  /CS /DeviceRGB) blends in that space (ISO 32000-2 §11.3.4). The
  interpreter now tells the device about such a group
  (Device::group_blends_outside_device_cmyk), and a pass that meets one
  declines, so the enclosing group is drawn as before. A non-isolated
  group's /CS is ignored (ISO 32000-2 §11.6.6, Table 145, CS: it takes its parent's space), so
  it stays in the enclosing group's CMYK.

Test: raster::tests::isolated_cmyk_groups_blend_at_any_size_and_respect_nested_spaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:20:28 -04:00
jpeskov 7f9530daf9 fix(render): an ICCBased CMYK colour is not taken as process tints; fix a patch number
Review of #3777. `Color::ink_tints` no longer gives tints for a
four-component ICCBased colour. Such a colour keeps its own profile, so a
group that holds one is drawn as before. vendor/README.md: the hayro
patch (21) entry refers to hayro-interpret patch (45), not (44).
2026-10-28 18:20:28 -04:00
jpeskovandClaude Opus 5.5 ba09e1aefc fix(render): an isolated DeviceCMYK transparency group blends in CMYK
ISO 32000-2 §11.3.4: the elements of a transparency group blend in the
group's colour space. For a subtractive space, each component is
complemented before the blend function and again after it. §11.3.5.3:
the nonseparable modes blend C, M and Y as their complements R, G and B,
and K comes from the backdrop (Hue, Saturation, Color) or from the source
(Luminosity). PdfCraft converted each colour to RGB and blended in RGB.
A print test file's blend-mode patch then showed an X in almost every
cell: magenta under black with Difference stayed magenta, not green.

hayro-interpret patch (45): CmykGroup and Device::draw_isolated_cmyk_group
(default false) for isolated, non-knockout groups with /CS /DeviceCMYK.
interpret_form_contents is now a function of its own. Color::ink_tints
and color::cmyk_to_srgb8 are new.

hayro patch (21): the renderer draws such a group as a CMY pass and a K
pass of complemented tints. It converts the tints to RGB with the CMYK
profile and composites the result through the group's layer. The group
is drawn as before with reading colours, for contents without process
tints (RGB, spot colours, patterns, images) and above 2^24 pixels.

Test: raster::tests::isolated_groups_blend_in_device_cmyk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:20:28 -04:00
aiongg dc943eeb13 Merge pull request #4718 from alehel/fix/marks-update-preview-without-old-marks
fix(ui): Update previews the page without the marks it replaces
2026-10-28 17:59:41 -04:00
aiongg 7cde655cc2 Merge pull request #4709 from alehel/fix/page-cursor-writer
fix(ui): the page sets its cursor once, from the top claim the tool reaches
2026-10-28 17:57:29 -04:00
aiongg f824475202 Merge pull request #4631 from Lcoding15/fix/delete-one-radio-button
M6: Delete removes one selected radio button, not its whole group (#4609)
2026-10-28 17:55:17 -04:00
.CooCoo 222a124650 M5: Import comments takes another PDF and copies its comments onto the same pages (#4595)
* M5: Import comments takes another PDF and copies its comments onto the same pages

Import comments read only XFDF, FDF, XML, XDP, CSV and text data files.
It now also takes a PDF: each comment of that PDF is copied onto the
page with the same number, at the same place, with its appearance
stream, its pop-up, its replies and its layer. Comments on pages past
the document's last page are left out, together with replies to them.
Links, form fields and redaction marks are not copied. A comment named
like one already on its page replaces it, as an XFDF or FDF import
does. A PDF without comments is refused with "nothing in the file
matches this document", and a PDF that needs a password is refused.

- organize: import_comments copies the comments with the page copier
  that Insert Pages uses, and registers the layers they use.
- engine: Edit::ImportData reads a file that starts with %PDF- as a PDF.
  Any other file goes to the data importer first, and is read as a PDF
  when that fails, %PDF- occurs in its first 1,024 bytes and the PDF
  reader opens it.
- ui: Comments > Import comments lists .pdf files; form.import_data
  lists the same files as before.
- automation: the doc_import_data description names the PDF case.
- parity: comment.import-from-pdf is shipped.

* Import comments: an annotation that is no comment keeps its name

A comment imported from a PDF replaced any annotation of the same /NM
on its page that was not locked, a redaction mark or a widget, so a
link, screen or 3D annotation of that name was overwritten. Only an
annotation whose subtype is one of the copied comment subtypes is
replaced now; for any other, the copy is added beside it without the
name. The subtype is read through an indirect reference.

* Import comments: index each page's annotations instead of scanning them per comment

* Import comments: a layer that BaseState OFF hides stays hidden

* Import comments: a PDF with binary bytes before its header imports on an XFA form, and two comments with one name both land

On an XFA form, a decode failure no longer stops the import when a PDF
header is in the first 1,024 bytes; the PDF's comments are copied. Two
incoming comments on one page with the same /NM no longer replace the
same comment: the first replaces it, and the second is added without
the name.

* Import comments: an inline annotation on the page is replaced by name

An annotation written inline in a page's /Annots is made an indirect
object before names are matched, so an incoming comment with its /NM
replaces it instead of being added beside it.

* Import comments: two new comments with one name keep it once

When no comment on the page has the name, the first incoming comment
keeps its /NM and each later one with the same name is added without it.
2026-10-28 17:53:03 -04:00
aiongg 4d8dc3a1b7 Merge pull request #4560 from Lcoding15/feat/bookmarks-from-text-styles
M4: New bookmarks from text styles, for headings that aren't tagged (#1693)
2026-10-28 17:50:47 -04:00
.CooCoo 8f3a4f18bd M6: Organize pages ▸ Page properties sets the selected pages' tab order (#4516)
* M6: Organize pages ▸ Page properties sets the selected pages' tab order

Page properties in the Organize pages panel was listed as planned. In
the app, a page's tab order could only be set for every page at once,
with the form.tab_order commands, which offer no Unspecified. (The
form_tab_order tool already set chosen pages, Unspecified included.)

- Page properties (page.properties) opens a Tab Order dialog for the
  selected pages, with Use Row Order, Use Column Order, Use Document
  Structure and Unspecified. OK writes Edit::SetTabOrder for those pages
  as one undo step. The dialog opens on the pages' order, or with no
  choice made when they differ, and then OK is off until one is chosen.
  OK on the order the pages already have adds no undo step.
- Rows and columns run right to left in a document bound on the right,
  as Use Row Order and Use Column Order are described. PdfCraft reads a
  right-to-left binding from /ViewerPreferences /Direction /R2L
  (ISO 32000-1, 12.2, Table 150). Before, rank_tabs ranked them left to
  right.
- A page whose /Tabs is an indirect object now takes its row, column or
  structure order. Before, rank_tabs read only a direct name and ranked
  the page in annotation order.
- pdfcraft_forms::tab_orders and Document::tab_orders read each page's
  /Tabs. form_tab_order returns them as page_tabs, and reads without
  changing anything when it is given no order or field.
- set_option("dialog", "page-properties") opens the dialog.
- Parity: organize.page-tab-order is shipped, and its notes say the
  Actions tab of Page Properties (organize.page-actions) is still
  planned; the Organize pages grid notes list Page properties.

* Page properties: OK applies only to the document the dialog was opened on

The draft keeps the document's id. When another document is active by
OK, the dialog closes without changing either document.

* Menus: list Page properties… in the Pages menu

The menu bar now lists its commands explicitly, and every command with a menu group must appear in it.

* Page properties: apply the tab order only to an unchanged document

An edit, undo or redo made while the dialog is open can move the pages
at the stored indices. OK now closes the dialog without changing a page
when the document's edit generation differs from when it opened.

* Page properties: say when a document change cancels the tab order

OK shows a notice when the document changed or another document became
active while the dialog was open, so the user knows to open it again.
2026-10-28 17:48:29 -04:00
Eric Kaden 21f6c29d3e M6: form script messages keep their title, icon and buttons, and every one is shown (#4293, #4294) (#4306)
* M6: form script messages keep their title, icon and buttons, and every one is shown (#4293, #4294)

* M6: a form script message waits for a notice the toast already shows

* M6: form script messages past the queue's room are counted and reported

* M6: a sentence break in the merged parity note

* M6: an object that names no parameter is a message's text, as a String object or a caught error

* M6: the count of left-out script messages is said again if another notice replaces it before its time

* M6: a full stop between two sentences of the FormCalc parity note

* chore: re-run CI (a frame-budget timing test missed on a shared runner)
2026-10-28 17:46:11 -04:00
aiongg 31f51824c8 Merge pull request #2838 from jonathanwxh-cell/fix/cropbox-text-clipping
fix(render): clip extracted text to the page CropBox (#2670)
2026-10-28 17:43:51 -04:00
Lucas Sampaio Dias 03996b1025 Improve reading-colour preferences and add a settings shortcut (#2627)
* M3: improve reading-colour preferences and add settings shortcut

* M3: clarify the disabled reading-colour preview

* M3: adapt rebased preferences to current UI dependencies
2026-10-28 17:41:29 -04:00
oracola91 40c22e3ea5 Export Data as CSV keeps formula-like cells as text, like the merge report (#2524)
Export Data (CSV) wrote field names and values as they are, so a value
from a received form that starts with = or @ (or + or - when it is not a
number) runs as a formula when the CSV is opened in a spreadsheet.

It now uses the rule merge_csv got in #4101, moved unchanged into
runs_as_formula, so the merge report's output is the same. Import Data
takes the added ' off again, so Export Data then Import Data gives back
every value unchanged.
2026-10-28 17:39:06 -04:00
aiongg 750012c75f Merge pull request #2402 from wstock/fix/2368-flatten-no-rotate
Issue #2368: flattening keeps a NoRotate widget upright on a rotated page
2026-10-28 17:36:43 -04:00