diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md
index b4337b2a66..9afb1f3537 100644
--- a/ATTRIBUTION.md
+++ b/ATTRIBUTION.md
@@ -4,12 +4,14 @@
Every asset PrintCraft includes, bundles or uses to build its published material, with its author, source and licence. The policy is in [AGENTS.md](AGENTS.md) §1. The machine-readable list, with SHA-256 hashes, is [ATTRIBUTION.toml](ATTRIBUTION.toml). Licence texts are kept beside the assets and summarised in [NOTICE](NOTICE).
-## In this repository (213)
+## In this repository (215)
| Asset | Title | Author | Licence | Source | Used for |
|---|---|---|---|---|---|
| `assets/icons/accessibility.svg` | Lucide icon "accessibility" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/accessibility.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
| `assets/icons/align-left.svg` | Lucide icon "align-left" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/align-left.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
+| `assets/icons/align-center.svg` | Lucide icon "align-center" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/align-center.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
+| `assets/icons/align-right.svg` | Lucide icon "align-right" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/align-right.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
| `assets/icons/app-window.svg` | Lucide icon "app-window" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/app-window.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
| `assets/icons/arrow-left-right.svg` | Lucide icon "arrow-left-right" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/arrow-left-right.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
| `assets/icons/arrow-up-down.svg` | Lucide icon "arrow-up-down" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/arrow-up-down.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
diff --git a/ATTRIBUTION.toml b/ATTRIBUTION.toml
index db28bd6f24..f87e63afba 100644
--- a/ATTRIBUTION.toml
+++ b/ATTRIBUTION.toml
@@ -30,6 +30,28 @@ kind = "icon"
usage = "UI icon, embedded via crates/ui-egui/src/icon_data.rs"
sha256 = "30d5abcbed2176ee33b2844c8cdde6fc379c3e9ec28a6a22a1fa916a356c1dcb"
+[[asset]]
+path = "assets/icons/align-center.svg"
+title = "Lucide icon \"align-center\""
+author = "Lucide Contributors (portions Cole Bemis, Feather)"
+source = "https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/align-center.svg)"
+licence = "ISC"
+licence_file = "assets/icons/LICENSE-lucide.txt"
+kind = "icon"
+usage = "UI icon, embedded via crates/ui-egui/src/icon_data.rs"
+sha256 = "bc70fba645343be0760d1a87f9e0975a10ead2b8d95883314f4cea007376e79f"
+
+[[asset]]
+path = "assets/icons/align-right.svg"
+title = "Lucide icon \"align-right\""
+author = "Lucide Contributors (portions Cole Bemis, Feather)"
+source = "https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/align-right.svg)"
+licence = "ISC"
+licence_file = "assets/icons/LICENSE-lucide.txt"
+kind = "icon"
+usage = "UI icon, embedded via crates/ui-egui/src/icon_data.rs"
+sha256 = "70ef15bcaa5721820aef032ad1e65370fb2667fd2e2d63eb24fbf3c2f57346cc"
+
[[asset]]
path = "assets/icons/app-window.svg"
title = "Lucide icon \"app-window\""
diff --git a/assets/icons/align-center.svg b/assets/icons/align-center.svg
new file mode 100644
index 0000000000..8204bfedc9
--- /dev/null
+++ b/assets/icons/align-center.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/assets/icons/align-right.svg b/assets/icons/align-right.svg
new file mode 100644
index 0000000000..5e756411dc
--- /dev/null
+++ b/assets/icons/align-right.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/crates/automation/src/content.rs b/crates/automation/src/content.rs
new file mode 100644
index 0000000000..454c33a0b7
--- /dev/null
+++ b/crates/automation/src/content.rs
@@ -0,0 +1,167 @@
+//! Edit a PDF ▸ Add content tools: add text and images as page content, list what was added,
+//! move/resize/retype/restyle it, delete it. Geometry is in points from the top-left of the
+//! displayed page (y down), like every other tool.
+
+use printcraft_engine::{AddedContent, AddedText, Edit, FontFamily, TextAlign};
+use serde_json::{Value, json};
+
+use crate::comments::parse_color;
+use crate::{Args, Automation, Result, ToolError, failed};
+
+fn bad(m: impl Into) -> ToolError {
+ ToolError::InvalidArgs(m.into())
+}
+
+/// Top-left view rect [x0 y0 x1 y1] ↔ display space [x0 y0 x1 y1] (y up).
+fn to_display(r: [f64; 4], ph: f64) -> [f64; 4] {
+ [r[0].min(r[2]), ph - r[1].max(r[3]), r[0].max(r[2]), ph - r[1].min(r[3])]
+}
+
+fn to_view(r: [f64; 4], ph: f64) -> [f64; 4] {
+ [r[0], ph - r[3], r[2], ph - r[1]]
+}
+
+fn rect_arg(a: &Args, key: &str) -> Result