Files
pdfcraft/crates/xfdf
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
..

pdfcraft-xfdf

Layer L4: comment and form data exchange (execution plan M5.6, M6.7).

let xml = export_xfdf(&doc, true, true, "form.pdf");   // comments and/or field values
let fdf = export_fdf(&doc, false, true, "form.pdf")?;
let txt = export_data(&doc, Format::Txt);               // or Xml, Csv
let report = import(&mut doc, &bytes)?;                 // XFDF, FDF, XML, CSV or text, detected
  • XFDF (ISO 19444-1): every comment type PdfCraft knows (text, free text, line, square, circle, polygon, polyline, markup, stamp, caret, ink, attachments, sound, redact) with rect, name, author, subject, dates, flags, colours, opacity, width, icon, quads, line ends, ink, default appearance, callouts (callout, fringe, intent, head), pop-ups and reply threads (inreplyto); field values nested by name, with text preserved across XML comments and processing instructions.
  • FDF: fields (with /Kids hierarchies) and indirect comments. Nested annotation values are copied under FDF object numbers; shared references and cycles remain shared. Document links and appearances are excluded. Source /Page and /Pages dictionaries become null at every copy depth, so nested destination actions cannot carry the source page tree or content. Type names may be indirect; lookup checks each reference generation, is iterative, stops at cycles and refuses chains beyond 64 references. Discovery borrows direct arrays and dictionaries, retains array owners lazily and records annotation positions instead of resolved bodies. Retained entries are budgeted before their keys or values are cloned, including when saved-PDF cache eviction causes annotation bodies to be parsed again. Export returns Result and refuses copied graphs above 100,000 comments/objects/values, 64 direct nesting levels or a conservative 64 MiB serialization budget instead of exporting partial data. These are copied-graph limits: source parsing and page discovery retain their existing COS/parser limits, with no total source-memory bound. Unknown indirect streams retain their encoded bytes. FDF import semantics are unchanged.
  • Form data: Acrobat's XML (xfdf:original keeps names that aren't XML names), CSV and tab-delimited text (a row of names, a row of values).

XML and XFDF exports preserve tabs and line breaks in field names and comment metadata with character references, so XML attribute normalization cannot change their identities.

Plain AcroForm XML imports retain all direct text around comments and processing instructions, including CDATA and character references. Nested-element and XFA interpretation are unchanged.

CSV export and spreadsheet merging quote tabs, and import distinguishes quoted tabs from TSV delimiters without changing field identities. Automatic detection of externally authored, ambiguous tables remains heuristic. Both write a cell that a spreadsheet would run as a formula (= or @, or + or - when it isn't a plain number) with a leading '; CSV import takes off the ' that export added, so exported data imports back unchanged.

Import replaces comments with the same /NM (and drops the replaced comment's pop-up), draws appearances PdfCraft can draw, and sets values through the form's own checks (formats, validation, recalculation); read-only fields take imported values, as in Acrobat. XML is read with roxmltree.