diff --git a/crates/engine/src/cmd/datamerge.rs b/crates/engine/src/cmd/datamerge.rs index 6fac73836..411fd545c 100644 --- a/crates/engine/src/cmd/datamerge.rs +++ b/crates/engine/src/cmd/datamerge.rs @@ -503,6 +503,9 @@ fn add_placeholder(d: &mut Document, sel: &Selection, p: &Value, name: &str) -> } else if field.kind == DataFieldKind::Image { // An image field at the insertion point places the image inline in the text. PlaceholderRole::Image + } else if field.kind == DataFieldKind::Qr { + // A QR field at the insertion point is refused below: QR codes bind to a frame. + PlaceholderRole::Qr } else { PlaceholderRole::Text }; diff --git a/crates/engine/src/cmd/datamerge/tests.rs b/crates/engine/src/cmd/datamerge/tests.rs index 8ca5291a6..39b1988e2 100644 --- a/crates/engine/src/cmd/datamerge/tests.rs +++ b/crates/engine/src/cmd/datamerge/tests.rs @@ -817,6 +817,20 @@ fn an_image_field_at_the_insertion_point_merges_as_an_inline_image() { assert!((wide.1 - width).abs() < 0.01 && (wide.2 - width / 8.0).abs() < 0.01, "a wide image is scaled down to the frame: {wide:?}"); } +/// A QR field at a text insertion point is refused instead of merging its payload as text. +#[test] +fn a_qr_field_at_the_insertion_point_is_refused() { + let mut s = Session::new(); + new_doc(&mut s, 1); + select_csv(&mut s, "@Photo,#Code\n/x.png,hello\n"); + let made = text_frame(&mut s, [36.0, 36.0, 300.0, 300.0], "Hi ", None); + exec(&mut s, "text.select", json!({"story": made["story"], "anchor": 3, "focus": 3})); + let msg = err(&mut s, "data.placeholder.add", json!({"field": "Code"})); + assert!(msg.contains("bind to a frame"), "{msg}"); + assert_eq!(texts(&s), vec!["Hi ".to_string()]); + assert!(s.documents()[0].doc.data_merge.placeholders.is_empty()); +} + /// A typed `<>` marker for an image column places the image too, from an absolute path; /// an explicit text role still writes the path as text. #[test] diff --git a/docs/agents.md b/docs/agents.md index d2f416c83..13977d2af 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -90,9 +90,10 @@ external PDF/X certification. `data.source.remove` drops it); `data.fields` lists its fields. `data.placeholder.add {field, role?, story?, at?, end?, item?}` marks text or a frame as a field (`role`: text, image, qr, hyperlink). An image field (a `@` column) inserted at a text insertion point, or typed there as `<>`, merges as an inline image at its natural size, scaled down to fit the -frame. An image path is absolute or relative to the data file's folder, then the document's; a path that starts with -one separator (`\Photos\a.jpg`) is tried from the drive's root first. `data.options` sets records, tiling and image -fitting; `data.preview {record}` / `data.preview.stop` show one record without saving it. +frame. A QR field (a `#` column) inserted at a text insertion point is refused; bind it to a frame instead. An image +path is absolute or relative to the data file's folder, then the document's; a path that starts with one separator +(`\Photos\a.jpg`) is tried from the drive's root first. `data.options` sets records, tiling and image fitting; +`data.preview {record}` / `data.preview.stop` show one record without saving it. `data.merge` now **creates a new document** and leaves the template unchanged (the old `spread` parameter, which appended pages to the template, is an error). It uses the linked source, or inline `csv` / `rows` / `path` / `bytes`: