From 824837915205ee9bd19de641ee6597d0b8adf2c2 Mon Sep 17 00:00:00 2001 From: mtlcl410 Date: Thu, 15 Oct 2026 14:29:16 +0800 Subject: [PATCH] fix(forms): new text and date fields take Acrobat's Auto font size (#1888) (#1912) add_field gave text and date fields a fixed /Helv 12 Tf, which is clipped in a box shorter than 12 pt, and a script can't see the clipping. They now get /Helv 0 Tf (Auto), as Acrobat's new text fields do: 12 pt where there is room, smaller where there isn't. An explicit size set afterwards still wins; choice fields keep 12 pt. Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz Co-authored-by: Claude Fable 5.1 --- crates/forms/src/author.rs | 10 ++++++---- crates/forms/src/tests.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/crates/forms/src/author.rs b/crates/forms/src/author.rs index 6d49be100..0b60506c7 100644 --- a/crates/forms/src/author.rs +++ b/crates/forms/src/author.rs @@ -1,8 +1,9 @@ //! Prepare a form: create, change and delete fields (execution plan M6.6). //! //! New fields follow Acrobat's conventions: names "Text1", "Check Box1", "Group1" (radio groups), -//! "Dropdown1", "List Box1", "Button1", "Signature1", "Date1"; 12 pt Helvetica (auto-size for -//! check boxes), a thin grey border and a white background, and appearance streams generated +//! "Dropdown1", "List Box1", "Button1", "Signature1", "Date1"; Helvetica at Acrobat's Auto size +//! for text and date fields (12 pt in a box that has room, smaller in a short one), 12 pt in +//! choice fields, a thin grey border and a white background, and appearance streams generated //! immediately so every viewer shows the empty field. use pdfcraft_cos::{Dict, Document, ObjRef, Object, PdfString, Stream}; @@ -545,14 +546,15 @@ pub fn add_field(doc: &mut Document, page: usize, rect: [f64; 4], kind: &NewFiel match kind { NewField::Text { multiline } => { w.set(b"FT".to_vec(), Object::name("Tx")); - w.set(b"DA".to_vec(), PdfString::literal(b"/Helv 12 Tf 0 g".to_vec())); + // Auto size (0): a fixed 12 pt is clipped in a box shorter than that (#1888). + w.set(b"DA".to_vec(), PdfString::literal(b"/Helv 0 Tf 0 g".to_vec())); if *multiline { w.set(b"Ff".to_vec(), Object::Int(flags::MULTILINE as i64)); } } NewField::Date => { w.set(b"FT".to_vec(), Object::name("Tx")); - w.set(b"DA".to_vec(), PdfString::literal(b"/Helv 12 Tf 0 g".to_vec())); + w.set(b"DA".to_vec(), PdfString::literal(b"/Helv 0 Tf 0 g".to_vec())); // Acrobat's format and keystroke actions for mm/dd/yyyy (run by its JavaScript engine). let js = |s: &str| { let mut a = Dict::new(); diff --git a/crates/forms/src/tests.rs b/crates/forms/src/tests.rs index 0256bfafb..d434957eb 100644 --- a/crates/forms/src/tests.rs +++ b/crates/forms/src/tests.rs @@ -895,6 +895,34 @@ fn formats_validation_and_calculations_run_like_acrobat() { assert_eq!(field(&fields(&doc), "Price").actions.format, Format::None); } +#[test] +fn new_text_fields_fit_their_box() { + // #1888: a fixed 12 pt was clipped in a short box. New text and date fields take Acrobat's + // Auto size: 12 pt where there is room, smaller where there isn't. + let mut doc = one_page(); + let tf_size = |ap: &str| { + ap.split("/Helv ") + .nth(1) + .and_then(|s| s.split(' ').next()) + .and_then(|n| n.parse::().ok()) + .unwrap_or_else(|| panic!("no /Helv size in {ap}")) + }; + for (kind, name, value) in [(NewField::Text { multiline: false }, "short", "Hello world"), (NewField::Date, "date", "10/13/2026")] { + add_field(&mut doc, 0, [20.0, 20.0, 200.0, 28.0], &kind, Some(name)).unwrap(); + set_value(&mut doc, name, &FieldValue::Text(value.into())).unwrap(); + let f = field(&fields(&doc), name).clone(); + assert_eq!(f.da, "/Helv 0 Tf 0 g", "{name}"); + let size = tf_size(&ap(&doc, &f.widgets[0])); + assert!(size > 0.0 && size <= 8.0, "{name}: {size} pt in an 8 pt box"); + } + add_field(&mut doc, 0, [20.0, 50.0, 200.0, 72.0], &NewField::Text { multiline: false }, Some("tall")).unwrap(); + set_value(&mut doc, "tall", &FieldValue::Text("Hello world".into())).unwrap(); + assert_eq!(tf_size(&ap(&doc, &field(&fields(&doc), "tall").widgets[0])), 12.0, "a box with room keeps the 12 pt look"); + // An explicit size still wins. + set_props(&mut doc, "short", &FieldProps { font_size: Some(12.0), ..FieldProps::default() }).unwrap(); + assert_eq!(tf_size(&ap(&doc, &field(&fields(&doc), "short").widgets[0])), 12.0); +} + #[test] fn appearance_properties_restyle_every_widget() { let mut doc = one_page();