Commit Graph
1125 Commits
Author SHA1 Message Date
aiongg 4167ac6d5a Merge PR #4310: M11: honor page UserUnit when measuring image resolution 2026-10-24 06:21:23 +00:00
aiongg ce2000573b Merge PR #4155: M4: a save prompt that opens in the background asks for the user's attention 2026-10-24 06:21:22 +00:00
aiongg 4fedfe7e8c Merge PR #2648: fix(xfa): skip PDF fallback scripts while XFA layout is active
crates/engine/src/js.rs: kept main's DocInfo.xfa field and multi-line layout, added the PR's xfa_active gate on doc_scripts.
2026-10-24 06:21:20 +00:00
aiongg fa3a0221d9 Merge PR #3000: fix(edit): batch styled paragraph fallback glyphs 2026-10-24 06:21:12 +00:00
aiongg 6672e103a5 Merge PR #3009: fix(ui): the header, footer and watermark previews show the page under the marks (#2837) 2026-10-24 06:21:11 +00:00
aiongg ba85d2af66 Merge PR #2957: M6: dotted field names make parent fields (#2696) 2026-10-24 06:21:05 +00:00
mtlcl410andClaude Opus 5.5 f2e222ebc5 M2: a non-embedded CIDFont's fallback request carries its descendant's font descriptor (ISO 32000-2 §9.7.4, §9.8)
Type0Font::new built its FallbackFontQuery from the Type 0 font
dictionary, which has no /FontDescriptor (ISO 32000-2 §9.7.6.1,
Table 119); the descriptor belongs to the descendant CIDFont (§9.7.4.1,
Table 115). Every request for a non-embedded CID font therefore said
not serif, not italic, weight 400, and no resolver could tell a Song or
Mincho font from a Hei or Gothic one, or see /FontWeight 700 (§9.8,
Tables 120, 121).

FallbackFontQuery::with_descriptor takes the name from the font
dictionary and the style from the given descriptor; new() calls it with
the dictionary's own descriptor, so simple fonts are unchanged. The CID
path passes the descendant's descriptor it already reads.

Vendored hayro-interpret patch (58). Tests in
crates/render/tests/it/cid_fallback_query.rs record the request a
font_resolver receives. The CID-fonts parity note now says the Japanese
face also follows the Serif flag and /FontWeight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 13:31:31 +08:00
Combiler 9ff83883eb Merge branch 'main' into main 2026-10-24 04:40:50 +00:00
mtlcl410andClaude Opus 5.5 a6df1d3ca5 M2: Invisible annotations of unknown types and popups don't paint their appearance on the page (ISO 32000-2 Table 167)
An annotation whose Invisible flag (bit 1) is set and whose /Subtype is none
of the standard types of ISO 32000-2 Table 171 is no longer drawn (Table 167)
or printed (Table 167: "do not print it even if the Print flag is set"). A
/Popup annotation's /AP is no longer painted as page content: per §12.5.6.14 a
popup has no appearance stream of its own and is shown by the viewer as a
window for its parent. Printing already left popups out.

Link appearances are painted as before (§12.5.5).

hayro-interpret patch (56), recorded in vendor/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 12:06:49 +08:00
mtlcl410andClaude Opus 5.5 dd1a894b2d M6: leave the combo box drop-down arrow to #3567
The on-screen drop-down button for combo boxes drawn display-only, the
narrowed text area beside it, and the Print as Image copy without it
(with_printed_field_appearances, printed_display_bytes) are removed.
Open PR #3567 draws the arrow for every combo box in the page view.
Check boxes, radio buttons, push buttons and comb lines are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 12:01:55 +08:00
mtlcl410andClaude Opus 5.5 5233e12281 M6: check boxes, radio buttons and push buttons without an appearance are drawn, and comb fields show their cells (ISO 32000-2 Table 192, 12.7.5)
A form that leaves its appearances to the viewer had its text and choice
fields drawn for display (#3475), but not its buttons. Now, also for
display only, without changing the file:

- Check boxes and radio buttons with no normal appearance for their
  state show their /MK background and border (Table 192), and when on,
  the /MK /CA caption as a standard-14 ZapfDingbats character
  (12.7.5.2.3), 4 or l by default, in the /DA colour, auto-sized for
  0 Tf.
- Push buttons with none show their /MK background, their /BS border
  (beveled per Table 168) and their centred caption.
- Comb fields with a border colour draw the MaxLen - 1 lines between
  their positions (Table 228), up to 500 cells. This also applies to the
  appearances PdfCraft saves for comb fields.
- A combo box drawn for display shows a drop-down button on screen, with
  its text kept clear of it. Saved appearances and Print as Image leave
  it out (with_printed_field_appearances, printed_display_bytes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 11:51:00 +08:00
Eric Kaden 4d190aaff8 M12: the parity note and test say which sections lose their border 2026-10-23 22:52:53 -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 Fable 5.1 946fb31eef M3: restore last view settings when reopening documents (#1767)
A file opened again shows the page, zoom and page display it was last
shown with, as Acrobat's "Restore last view settings when reopening
documents" does: remembered by path (the last 100 files) when its tab
closes or settings are saved with it open, applied over the file's own
initial view, on by default and switched off in Preferences ▸ Documents
and view.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 10:09:47 +08:00
Eric Kaden d7da5dfe29 M12: a flowed XFA subform a page break cuts gets a closed border on each page, its margins repeated 2026-10-23 21:54:55 -04:00
mtlcl410andClaude Opus 5.5 d64a698825 fix(parity): cite the booklet print test at its path under tests/it (#2707)
main moved the ui-egui integration tests into tests/it, so xtask parity
could no longer find print.booklet's ui-egui evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 09:39:26 +08:00
mtlcl410andClaude Opus 5.5 184e9f30ff M10: a booklet decides two-sided itself, as Acrobat does (#2707)
dialog hides Two-sided while Booklet is chosen and prints the booklet two-sided itself
(Using Acrobat X Pro, Print dialog box options: "Acrobat automatically enables duplex
printing, if available, for booklet printing"; later manuals and the current help no
longer say so, and on a current Acrobat the Two-sided control is shown for Size and
hidden for Booklet with the same duplex-capable printer). print::booklet_duplex gives a
booklet's setting: both sides flip on the short edge, the fold of a 2-up sheet turned
sideways; front or back only is one pass of a manual duplex and prints one-sided. The
Print dialog hides Two-sided while Booklet is chosen and sends that setting, leaving the
Two-sided choice as it was for the other layouts (and as #2001 remembers it); doc_print
applies the same rule. This replaces the first version, which rewrote Two-sided to the
short edge when Booklet was chosen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 09:27:26 +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
mtlcl410andClaude Opus 5.5 b12965ea8e M6: the date picker has a Today button (#4274)
The calendar under a date field had no way to pick today: from another
month the only way back was the arrows. A Today button now sits under
the days, centred under the grid. It picks today from whichever month
the calendar shows, exactly as a click on today's cell would: written
with the field's own format through format_date, then the same commit
path, so the field's scripts treat it as they treat a clicked day, the
pick is one undoable edit, and the field and its calendar close. A year the field's date can't hold offers
no button. Hovering it shows the full date. One UI string in all 33
catalogues, marked AI-assisted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
2026-10-24 07:39:44 +08:00
Eric Kaden f5e74991ed M12: flowed XFA subforms are drawn with their border and fill 2026-10-23 19:30:43 -04:00
Eric Kaden 64bdea4e61 M6: fields that don't scroll stop taking text when full 2026-10-23 19:25:32 -04:00
nlevaandOpus 5.5 medium ultracode 5515f321c8 M7: copy added JPEG 2000 images as their file (#2636)
Review: copying an added JPEG 2000 image failed even within its own
document, because the exporter can't write JPEG 2000 yet. Add Image stores
such a picture as the file it was given, so that file is the copy: it
pastes into the same document as the same object and into another as one
embedded image. The notes now also say that a CMYK picture stored
losslessly turns to RGB in another document.

Co-Authored-By: Opus 5.5 medium ultracode <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEgu9NcTcPqKovsLyfPxK
2026-10-24 01:17:34 +02:00
nlevaandOpus 5.5 medium ultracode 302ad68502 M7: paste added text boxes and images in the engine and through content_copy (#2636)
Edit::PasteContent pastes an added text box or image copied in the same
document or another, as it was (a text box's style; an image's picture,
turns, flips and crop), as one undo step. A pasted image draws its picture
from the copied object while that is still the picture in the target, else
from an added image there that already holds it (an earlier paste from
another document), else the file is embedded. Document::added_image_file
gives a copied image's picture as a file and honours the copy permission.

pdfcraft_edit::added::paste_rect / paste_place place a paste where the item
was, 18 pt down and to the right past each copy of it on the page (an image
with the same box and transforms counts whichever object holds its
picture), or at a point, kept on the page.

The content_copy tool drives it headlessly, across pages and documents.

The desktop keys and menus come in follow-up PRs.

Co-Authored-By: Opus 5.5 medium ultracode <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEgu9NcTcPqKovsLyfPxK
2026-10-24 01:17:34 +02:00
Philip 0ad7396cf7 Merge remote-tracking branch 'origin/main' into feat/edit-scanned-page-offers-ocr 2026-10-24 00:02:56 +01:00
Osvaldo Duran MelendezandClaude Opus 5.5 8b2cefc04c Merge upstream main a2ec27b4 into pr/attachments: translation catalogs and notes kept from both sides, ATTRIBUTION hashes recomputed (conflicted: nothing)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:16:24 -04:00
alehelandClaude Opus 5.5 3485e92a95 fix(edit): backdrop copies keep no reference that outlives a redaction
The tag on a page's backdrop copy, `/PCBackdrop << /Original ref /At /Len >>`, kept the
page's original content stream reachable. After Add background, then Redact (or Remove
Hidden Information, or Edit text) and a full save, the text taken off the page was still in
the file, in the original stream the tag pointed at. Text extraction didn't show it; reading
the file's bytes did. Restore also gave up after any edit, so the fill stayed off without a
word.

The tag is now `/PCBackdrop << /Index n /Op (f) /Before <SHA-256> /Was [num gen] >>`: how many
of the copy's operators come before the `n`, the fill it stands in for, a hash of those
operators by value (redaction writes the stream out again with other spacing), and the
original's number as two integers, which keep nothing alive.

Remove background turns the fill back on in the copy when the operators before it still
hash the same and a scan with the fill restored finds it painted first, covering the page.
Edits and redactions after the fill are kept. When the original stream is still there with
the same bytes, the page points at it again. Otherwise the copy stays as it is, and
Remove background says which pages keep their own backdrop off: `EditNotice::BackdropsLeftOff`
(engine), a toast in the app, `warnings` from `doc_remove_marks`. `edit_notices` holds
`EditNotice` now, with the undrawn-text notice as one of its kinds. The two new UI strings
are in all 33 catalogues.

`sha2` (already in the lock file) becomes a dependency of `pdfcraft-edit`.

Tests, each seen failing with its piece of the fix reverted:
- `the_tag_holds_no_reference_and_a_full_save_keeps_no_second_copy_of_the_page` and
  `removed_text_leaves_the_file_when_a_background_replaced_the_pages_backdrop` (automation,
  redaction and Remove Hidden Information through a full save): fail with `/Original` back.
- `a_fill_that_would_cover_content_painted_before_it_stays_off`: fails without the re-scan.
- `a_copy_written_out_again_with_other_spacing_still_comes_back` and the automation test:
  fail when the raw bytes are hashed instead of the operators.
- `an_edit_after_the_fill_is_kept_and_the_backdrop_still_comes_back`: fails under the old
  rule (only an unchanged copy comes back).
- `remove_background_names_the_pages_whose_own_backdrop_stays_off` (engine),
  `remove_background_warns_about_pages_whose_own_backdrop_stays_off` (automation),
  `remove_background_says_which_pages_keep_their_own_backdrop_off` (UI): fail without the
  notice.
- `a_change_before_the_fill_leaves_it_off_and_names_the_page`,
  `a_tag_that_does_not_add_up_is_left_alone` (hostile and old-format tags, never a panic),
  `operators_hash_by_value_not_by_spelling`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 23:58:42 +02:00
Kyle Kassen e373dffa20 M8: redact labelled Italian international slash numbers 2026-10-23 16:37:10 -05:00
Kyle Kassen 50b5e90d63 M10: apply soft-mask Decode intervals to image exports 2026-10-23 16:37:10 -05:00
Kyle Kassen 9dc4bd598c M10: preserve table text from covered export slots 2026-10-23 16:37:10 -05:00
Kyle Kassen 30d85bbf15 M10: write DOCX cell widths from their grid columns 2026-10-23 16:37:10 -05:00
Kyle Kassen a8839bc5b1 M10: preserve page artwork when printing annotations 2026-10-23 16:37:10 -05:00
Kyle Kassen bf86689fd7 M11: measure optimizer image resolution in physical page units 2026-10-23 16:37:10 -05:00
Eric Kaden 3cf6e5f716 M12: a link's pieces don't join across a rule, picture or field between them 2026-10-23 17:05:18 -04:00
Eric Kaden d91028a990 M12: a link's address is shared by its runs, words and spans, not copied 2026-10-23 16:47:19 -04:00
Lcoding15 a4c5e5f9ee M7: the stand-in note names every standard family it can use (#4280)
CodeRabbit on #4316: the edit.font-reuse-substitution note said Helvetica
stands in for missing glyphs, but the face follows the source font's
family (Helvetica, Times or Courier) and its weight and slant.
2026-10-23 22:46:57 +02:00
Lcoding15 0b3b243e9a M7: a bold paragraph's stand-in font no longer turns earlier edits bold (#4280)
Edit text set a paragraph whose font lacks a typed character (an accent
missing from a subset) in a standard font, always under the resource
name PCEdHelv, whatever face it stood for. Rewriting a bold paragraph
after a regular one replaced PCEdHelv with Times-Bold, so every
paragraph set in the earlier stand-in turned bold, and the editor's
draft measurement saw those paragraphs change and grew its frame over
them.

The stand-in is now named after its face (PCEdTimesBold, ...), as the
line and region rewrites already name theirs.

Fixes #4280
2026-10-23 22:46:56 +02:00
alehel e38b129450 Merge remote-tracking branch 'origin/main' into fix/background-hidden-by-page-backdrop
# Conflicts:
#	ATTRIBUTION.toml
2026-10-23 22:44:29 +02:00
Eric Kaden 0008a18345 M12: XFA borders and rectangles with rounded corners are drawn rounded 2026-10-23 16:41:22 -04:00
Eric Kaden 1c4a481ea3 M12: round XFA check buttons draw a round background and border 2026-10-23 16:19:43 -04:00
Eric Kaden 0b8758eda3 M12: links in XFA rich text become clickable links 2026-10-23 16:16:40 -04:00
aiongg 8d99a5ce1f Translations: a missing string never fails a test; xtask ci prints the gaps (#4302)
Delete the translation coverage tests and the tests of one language's
wording. Keep catalog validation (format, duplicates, placeholders, plural
forms, command ids) and the tests that use a language (registration,
plural rules, RTL order, glyphs, casing, font fallback).

cargo xtask i18n-report runs i18n::tests::translation_gaps_report, which
prints each language's missing count and first few keys and always
passes; xtask ci runs it after the tests.
2026-10-23 15:42:18 -04:00
Marco Arnulfo c5b0e6aa61 M6: Actions tab: replacing a trigger's first action keeps its chain, and the row shows it (#4006)
The actions chained after a trigger's first one (/Next) are kept
whatever replaces it, not only another script, and the row reads
"…, then N more actions". Edit is disabled on every row while a
script is open, so it can't be overwritten.
2026-10-23 21:02:47 +02:00
Lcoding15andClaude Opus 5.5 21bbae39b9 M3: a link without a position keeps the reader's place (ISO 32000-2 §12.3.2.2)
A /XYZ destination with a null (or unusable) left or top went to the top of the
target page. ISO 32000-2 §12.3.2.2 says a null value keeps the current one, and
Acrobat Reader, observed on a synthetic two-page fixture at 400 %, keeps the
user-space point at the window's top-left on the new page at the same zoom.
CAD exports (WSCAD) write every cross-reference as [page /XYZ null null 0], so
following one zoomed in landed on the sheet's title row instead of where the
reader was looking.

go_to_dest now reads the place at the window's top-left before the jump and
uses it for the null coordinates, along each axis the page overflows; a page
that fits keeps the layout's own placement. Null /FitH top and /FitV left
still go to the page edge.

Tests: a unit test (zoomed, partly given, view turned a quarter, page that
fits) and an end-to-end click on such a link at 400 %, both seen failing with
the fix reverted. The malformed-bookmark case in
bookmark_destinations_scroll_to_their_position now expects the kept place, as
DestView documents unusable operands as null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:41:06 +02:00
Marco Arnulfo 61fe4b56a7 M6: Field Properties ▸ Actions: Edit opens an existing script (#4006)
Edit on a Run a JavaScript action loads its script into the editor;
Update replaces it on the same trigger, Discard leaves it. Saving the
tab now keeps actions that did not change as they are, and an edited
script keeps the actions chained after it (/Next).
2026-10-23 20:28:13 +02:00
Eric Kaden 441d9005e3 M12: XFA text is measured at its standard-14 font's own widths 2026-10-23 14:08:42 -04:00
srt8hellcat2-ui 17e2c35fdc Note the full-rewrite save on the Pages panel entry (#3407) 2026-10-23 13:14:11 -04:00
aiongg cf0f80604f Allow OFL typefaces, Noto CJK and Source Han included (AGENTS.md §1.1) (#3908)
* Allow OFL typefaces whoever designed them (AGENTS.md §1.1)

Noto CJK and Source Han from their open-source releases are allowed like any
other OFL font. The fonts build no longer skips them, the asset check accepts
an OFL font credited to Adobe, and the docs describe the Chinese faces that
result. The ban on Adobe icons, artwork, UI graphics and the fonts Adobe
products install stays.

* Leave out whole CJK faces other than the Japanese ones for size

A Chinese or Korean face is embedded whole only under 4 MiB, so the 16.4 MB
Noto Sans CJK SC in craft-fonts stays out of the binary and the embedded
total against the pinned craft-fonts is unchanged (25,204,828 bytes).

* docs: replacement boxes for Chinese without craft-fonts only on the web or without an installed Chinese face

* xtask assets: the OFL font exemption needs at least one licence ID

* fonts build: count a CJK script tag with a region (Hant-HK) as CJK, shared with the embedding test

* AGENTS.md: the asset gate exempts OFL fonts from the Adobe-author check

* fonts build: count a Hangul-only face (Hang) as CJK for the size rule

* fonts: the CJK embedding test checks the size rule, sharing its constants with build.rs

* docs: the missing Hans face is a property of the pinned craft-fonts, not of every build
2026-10-23 13:02:56 -04:00
Osvaldo DuranandClaude Fable 5.1 9eff0d2bed Parity: evidence paths follow the tests moved into tests/it
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-23 12:24:20 -04:00
Combiler e85c654ead Merge branch 'main' into main 2026-10-23 16:06:49 +00:00
RiceDee 7e96230a12 M7: parity notes say a justified row in a mixed-style region isn't stretched 2026-10-24 02:34:48 +11:00