mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-11 09:34:47 +00:00
M12: an unnamed XFA field's made-up name never takes one the template uses
An unnamed field or group is named `field` or `group`. When it came before a field the template names `field`, it took that name and the named field became `field_2`. A fallback name now skips the template's own names too, and a field keeps the name the template gives it while no other field has it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CBVyQEwtmEw3mS1MoAiwrK
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ca413b7e9f
commit
a03446e8dd
@@ -259,7 +259,7 @@ impl Layouter<'_> {
|
||||
let selected: Option<String> =
|
||||
self.data.and_then(|d| d.text_at(&data_path(self.tpl, &ctx.som))).map(|t| t.trim().to_string()).filter(|t| !t.is_empty());
|
||||
let inner = rect.inset(&g.common.margin);
|
||||
let group = self.unique_name(g.common.name.as_deref().unwrap_or("group"));
|
||||
let group = self.unique_name(g.common.name.as_deref().unwrap_or("group"), g.common.name.is_none());
|
||||
let nodes: Vec<Node> = g.fields.iter().map(|f| Node::Field(Box::new(f.clone()))).collect();
|
||||
let placed: Vec<(usize, Rect)> = match g.layout {
|
||||
Layout::Tb => {
|
||||
@@ -319,14 +319,20 @@ impl Layouter<'_> {
|
||||
/// A field name made from `base` that no other field of the form has: the first keeps it,
|
||||
/// later ones get `_2`, `_3`, …, skipping a name another field already has or that the
|
||||
/// template gives another field (a template field named `Cell4_2` keeps it, and the second
|
||||
/// `Cell4` becomes `Cell4_3`).
|
||||
pub(super) fn unique_name(&mut self, base: &str) -> String {
|
||||
/// `Cell4` becomes `Cell4_3`). A `fallback` base, the name an unnamed field or group gets
|
||||
/// (`field`, `group`), is made up too: it doesn't take a name the template gives another
|
||||
/// field either, and a template field named `field` still gets it.
|
||||
pub(super) fn unique_name(&mut self, base: &str, fallback: bool) -> String {
|
||||
let base = clean_name(base);
|
||||
// The template's own name, while no other field has it.
|
||||
if !fallback && self.taken.insert(base.clone()) {
|
||||
return base;
|
||||
}
|
||||
loop {
|
||||
let n = self.names.entry(base.clone()).or_insert(0);
|
||||
*n = n.saturating_add(1);
|
||||
let candidate = if *n == 1 { base.clone() } else { format!("{base}_{n}") };
|
||||
let reserved = *n > 1 && self.template_names.contains(&candidate);
|
||||
let reserved = (fallback || *n > 1) && self.template_names.contains(&candidate);
|
||||
if !reserved && self.taken.insert(candidate.clone()) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ impl Layouter<'_> {
|
||||
(Ui::Button, Some(cap)) => ctx.para.apply(&cap.para).h_align,
|
||||
_ => fctx.para.h_align,
|
||||
};
|
||||
let name = self.unique_name(f.common.name.as_deref().unwrap_or("field"));
|
||||
let name = self.unique_name(f.common.name.as_deref().unwrap_or("field"), f.common.name.is_none());
|
||||
let som = som_path();
|
||||
let read_only = matches!(f.access, Access::ReadOnly | Access::Protected);
|
||||
// The data's value wins over the template's default.
|
||||
|
||||
@@ -4117,3 +4117,18 @@ fn a_made_up_field_name_never_takes_one_the_template_uses() {
|
||||
assert_eq!(got, Some("Cell4_2"), "{rows:?}: the template's own name is kept ({names:?})");
|
||||
}
|
||||
}
|
||||
|
||||
/// An unnamed field's made-up name (`field`) doesn't take the name a later field has in the
|
||||
/// template: the field the template names `field` keeps it, and the unnamed one before it takes
|
||||
/// the next free name.
|
||||
#[test]
|
||||
fn an_unnamed_field_never_takes_a_name_the_template_uses() {
|
||||
let body = r#"<field w="2in" h="0.3in"><ui><textEdit/></ui></field>
|
||||
<field name="field" w="2in" h="0.3in"><ui><textEdit/></ui></field>"#;
|
||||
let form = layout_xml(&one_area_form(r#"layout="tb""#, body)).unwrap();
|
||||
let all = widgets(&form.pages[0]);
|
||||
// In layout order: the unnamed field, then the one the template names `field`.
|
||||
let names: Vec<&str> = all.iter().map(|w| w.name.as_str()).collect();
|
||||
assert_eq!(names.get(1), Some(&"field"), "{names:?}");
|
||||
assert_ne!(names.first(), Some(&"field"), "{names:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user