mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 20:47:32 -06:00
Forms: simplified field notation written as real JavaScript, not AFMakeNumber(0) (#1995)
calculate_js wrote a field's simplified field notation as
"/** BVCALC expr EVCALC **/ event.value = AFMakeNumber(0);". PdfCraft reads the
comment, but a viewer that runs the script instead (Acrobat among them) computed
0. notation_js now writes Acrobat's own translation after the comment: each
field name becomes AFMakeNumber(this.getField("name").value), with \ escapes
in names honoured, numbers and operators kept.
Co-authored-by: Osvaldo Duran <338149330+regendorf-ux@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Osvaldo Duran
Claude Opus 5.5
parent
77640a030f
commit
9ed12cb6a8
+44
-1
@@ -446,7 +446,9 @@ pub fn calculate_js(c: &Calculate) -> Option<String> {
|
||||
js_str(op.code()),
|
||||
fields.iter().map(|f| js_str(f)).collect::<Vec<_>>().join(", ")
|
||||
)),
|
||||
Calculate::Notation(expr) => Some(format!("/** BVCALC {expr} EVCALC **/ event.value = AFMakeNumber(0);")),
|
||||
// Acrobat's own translation follows the comment, so a viewer that runs the script instead
|
||||
// of reading the comment computes the same value (a placeholder here showed 0).
|
||||
Calculate::Notation(expr) => Some(format!("/** BVCALC {expr} EVCALC **/ event.value = {};", notation_js(expr))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,6 +866,41 @@ pub fn validate(v: &Validate, value: &str) -> Result<(), String> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Simplified field notation as the JavaScript Acrobat writes after its `BVCALC` comment:
|
||||
/// each field name becomes `AFMakeNumber(this.getField("name").value)`, the rest stays.
|
||||
pub fn notation_js(expr: &str) -> String {
|
||||
let mut out = String::new();
|
||||
let cs: Vec<char> = expr.chars().collect();
|
||||
let mut i = 0;
|
||||
while let Some(&c) = cs.get(i) {
|
||||
if c.is_whitespace() || "+-*/()".contains(c) {
|
||||
out.push(c);
|
||||
i += 1;
|
||||
} else if c.is_ascii_digit() || c == '.' {
|
||||
while let Some(&d) = cs.get(i).filter(|d| d.is_ascii_digit() || **d == '.') {
|
||||
out.push(d);
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
let mut name = String::new();
|
||||
while let Some(&n) = cs.get(i).filter(|n| !(n.is_whitespace() || "+-*/()".contains(**n))) {
|
||||
// `\` escapes a space or an operator inside a name, as Acrobat writes them.
|
||||
if n == '\\'
|
||||
&& let Some(&next) = cs.get(i + 1)
|
||||
{
|
||||
name.push(next);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
name.push(n);
|
||||
i += 1;
|
||||
}
|
||||
out.push_str(&format!("AFMakeNumber(this.getField({}).value)", js_str(&name)));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Evaluate simplified field notation: + - * / and parentheses over numbers and field names
|
||||
/// (`\` escapes spaces and operators in names, as Acrobat writes them).
|
||||
fn eval_notation(src: &str, value_of: &dyn Fn(&str) -> f64) -> Option<f64> {
|
||||
@@ -1024,6 +1061,12 @@ mod tests {
|
||||
assert_eq!(parse_calculate(&calculate_js(&c).unwrap()), Some(c));
|
||||
let c = Calculate::Notation("a + b".into());
|
||||
assert_eq!(parse_calculate(&calculate_js(&c).unwrap()), Some(c));
|
||||
// The JavaScript after the comment is Acrobat's translation, so viewers that run it
|
||||
// instead of reading the comment compute the same value (not 0).
|
||||
assert_eq!(
|
||||
calculate_js(&Calculate::Notation("(Unit\\ Price * Qty) / 2".into())).unwrap(),
|
||||
"/** BVCALC (Unit\\ Price * Qty) / 2 EVCALC **/ event.value = (AFMakeNumber(this.getField(\"Unit Price\").value) * AFMakeNumber(this.getField(\"Qty\").value)) / 2;"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user