Files
pdfcraft/crates/forms
9a529a4fbe Preserve alternate appearances and indirect push-button values on redraw (#417)
* D5: README for cos and engine, and an honest cos status line

Thirteen crates have no README, although the session protocol tells every session to read the README of the crate it is touching. These are the two biggest: cos is where every hostile file lands first, and engine is the whole API surface a frontend gets.

Each states its layer and the rules that bind it, sketches the API against the real signatures, and ends with what is deliberately not done. The cos README spells out the clone-is-a-snapshot property and why both writers return bytes rather than taking a sink; the engine README spells out the four-step editing model that makes what you see what Save writes.

Also corrects the module doc on cos/src/lib.rs, which still said encryption and object-stream output for full saves were not done. Both shipped: security.rs handles R2-R6 with crypt filters and /EFF, and SaveOptions::object_streams defaults to true.

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

* Preserve alternate appearances and indirect push-button values

Keep rollover, down and custom appearance entries when normal appearances are regenerated, without modifying shared dictionaries. Resolve indirect button caption and layout values. Cover full and incremental saved output, and wait for raster readiness in the existing Linux scroll test.

Co-authored-by: Codex <codex@openai.com>

* M14: let neutral Windows locales fall through to display language

Co-authored-by: Codex <codex@openai.com>

* pdfcraft-cli --help lists `mcp` only when the build has it

Follow-up to #540: a build without the `mcp` feature (`--no-default-features`) still listed
`pdfcraft-cli mcp` in its usage, and `pdfcraft-cli mcp` then said "unknown command".

USAGE is now put together with `concat!`, and its `mcp` lines come from a macro that is empty
without the feature. In such a build `pdfcraft-cli mcp` says the MCP server was left out instead
of calling it an unknown command.

- tests/help.rs: usage_lists_mcp_only_when_built_with_it (runs in both feature sets) and
  mcp_without_the_feature_says_it_was_left_out (without the feature)

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

* engine README: the execution plan is local-only, so don't link a path that doesn't exist

---------

Co-authored-by: Tanish Manku <tmanku.dev@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Streetblock <github@streetblock.de>
Co-authored-by: lloyd <lloyd@stackworx.io>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 14:45:32 -04:00
..

pdfcraft-forms

Interactive forms (AcroForm, ISO 32000-2 §12.7). Layer L3; depends on pdfcraft-cos and pdfcraft-fonts.

API

let all: Vec<Field> = fields(&doc);                         // terminal fields with widgets, options, flags, /DA
set_value(&mut doc, "name", &FieldValue::Text("Ada".into()))?;
set_value(&mut doc, "agree", &FieldValue::Check(true))?;
set_value(&mut doc, "size", &FieldValue::Radio(Some("L".into())))?;
set_value(&mut doc, "toppings", &FieldValue::Choice(vec!["Ham".into()]))?;
reset(&mut doc, None)?;                                      // Clear form (back to /DV)

// Prepare a form
let name = add_field(&mut doc, 0, [72.0, 700.0, 216.0, 722.0], &NewField::Text { multiline: false }, None)?; // "Text1"
set_props(&mut doc, &name, &FieldProps { name: Some("email".into()), required: Some(true), ..Default::default() })?;
delete_field(&mut doc, "email")?;

Behaviour

  • Inheritable attributes (/FT, /Ff, /V, /DV, /DA, /Q, /MaxLen) are resolved down the field tree; the form's /DA and /Q are the last fallback.
  • Text and choice fields get new appearance streams (/Tx BMC … EMC): auto-size (0 Tf), comb, multiline wrapping, password masking, quadding, /MK background and border, list-box selection. The /DA font is used when /DR has it as a simple font; otherwise Helvetica (WinAnsi).
  • Check boxes and radio buttons switch /V and each widget's /AS. Widgets with no appearances get PdfCraft's own (a drawn check mark or dot; no symbol font needed).
  • Values are validated (options, MaxLen, single vs multi-select, NoToggleToOff, read-only) and a failed call changes nothing.
  • /RV (rich text) is removed when a value is set so it can't contradict /V.
  • New fields follow Acrobat: names Text1, Check Box1, Group1 (radio groups), Dropdown1, List Box1, Button1, Date1, Signature1; /Helv is added to /DR; appearances are drawn at once. Radio buttons joining a group become kid widgets with their own export state. Renaming changes only the last part of a dotted name.

Not yet

JavaScript actions (format, keystroke, validate, calculate; M6.4/M6.5), rich-text values, the Appearance (colours, fonts), Actions, Format, Validate and Calculate property tabs, FDF/XFDF import and export (M6.7). /MK /R rotation (0, 90, 180, 270) is drawn into the appearance.