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 <noreply@anthropic.com>
This commit is contained in:
mtlcl410
2026-10-15 02:29:16 -04:00
committed by GitHub
co-authored by Claude Fable 5.1
parent 1ae12090d0
commit 8248379152
2 changed files with 34 additions and 4 deletions
+6 -4
View File
@@ -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();
+28
View File
@@ -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::<f64>().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();