Integration: a short multi-line field accepts the line it draws (#4339)

The fit check required the padded height for every line; it now uses the bound the appearance applies, so a 16 pt field at 12 pt takes one line.
This commit is contained in:
aiongg
2026-10-24 08:17:01 +00:00
parent 06959df890
commit be826a3bcd
2 changed files with 33 additions and 2 deletions
+5 -2
View File
@@ -130,8 +130,11 @@ impl TextBox {
// The smallest an automatic size goes ([`auto_text_size_by`]).
let size = self.size.unwrap_or(4.0);
if self.multiline {
let lines = wrap_with(&shown, size, self.inner_w, width_of).len() as f64;
lines * size * 1.15 <= self.inner_h + 1e-6
// As the appearance draws it: the first baseline is 0.85 em below the padded top and
// a line is drawn while its descent (0.207 em) stays inside the border, so the
// padding below the last line is not needed: `inner_h` plus 2 pt.
let above = wrap_with(&shown, size, self.inner_w, width_of).len().saturating_sub(1) as f64;
above * size * 1.15 + 1.057 * size <= self.inner_h + 2.0 + 0.01
} else {
// A line break in a single-line value is drawn as a space.
let line: String = shown.chars().map(|c| if matches!(c, '\n' | '\r') { ' ' } else { c }).collect();
+28
View File
@@ -439,6 +439,34 @@ fn a_text_field_says_what_fits_it_as_its_appearance_draws_it() {
assert!(tb("comb").fits(&"W".repeat(50)));
}
/// #4339 review: a multi-line field is full when its appearance stops drawing lines (a line is
/// drawn while its descent stays inside the border), not when `lines x size x 1.15` outgrows the
/// padded height: a 16 pt field at 12 pt draws one line.
#[test]
fn a_short_multiline_field_takes_the_one_line_it_draws() {
let tx = |name: &str, extra: &str, h: i32| {
format!("<< /Type /Annot /Subtype /Widget /FT /Tx /T ({name}) /DA (/Helv 12 Tf 0 g) /Ff {}{extra} /Rect [50 {} 150 700] /P 3 0 R >>", flags::MULTILINE, 700 - h)
};
let objs: Vec<String> = vec![
"<< /Type /Catalog /Pages 2 0 R /AcroForm 4 0 R >>".into(),
"<< /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 600 800] >>".into(),
"<< /Type /Page /Parent 2 0 R /Annots [6 0 R 7 0 R] >>".into(),
"<< /Fields [6 0 R 7 0 R] /DA (/Helv 12 Tf 0 g) /DR << /Font << /Helv 5 0 R >> >> >>".into(),
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>".into(),
tx("plain", "", 16),
tx("boxed", " /MK << /BC [0] >>", 16),
];
let doc = document(&objs);
let all = fields(&doc);
let tb = |name: &str| {
let f = field(&all, name);
appearance::text_box(&doc, f, &f.widgets[0])
};
assert!(tb("plain").fits("one line") && !tb("plain").fits("one\ntwo"));
// A 1 pt border takes 1 pt from each side: the line's descent then falls outside it.
assert!(!tb("boxed").fits("one line"));
}
/// An edit to a field that doesn't scroll keeps as much of what it inserted as fits, wherever the
/// caret was, and always keeps a deletion.
#[test]