Skip to main content

Cell

Struct Cell 

Source
pub struct Cell<'a> { /* private fields */ }
Expand description

A mutable reference to a table cell.

Implementations§

Source§

impl<'a> Cell<'a>

Source

pub fn text(&self) -> String

Get the combined text of all paragraphs in this cell.

Source

pub fn set_text(&mut self, text: &str)

Set the text of the first paragraph (replacing existing content).

Examples found in repository?
examples/generate_pdf.rs (line 36)
7fn main() {
8    // Test 1: Simple document
9    let doc = Document::new();
10    match doc.to_pdf() {
11        Ok(bytes) => {
12            std::fs::write("/tmp/rdocx_simple.pdf", &bytes).unwrap();
13            println!("Simple PDF: {} bytes -> /tmp/rdocx_simple.pdf", bytes.len());
14        }
15        Err(e) => println!("Simple PDF failed: {e}"),
16    }
17
18    // Test 2: Document with content
19    let mut doc = Document::new();
20    doc.set_title("Test PDF Document");
21    doc.set_author("rdocx-pdf");
22    doc.add_paragraph("Chapter 1: Introduction")
23        .style("Heading1");
24    doc.add_paragraph(
25        "This is a test document generated by rdocx and rendered to PDF. \
26         It demonstrates text rendering with proper font shaping and pagination.",
27    );
28    doc.add_paragraph("Section 1.1").style("Heading2");
29    doc.add_paragraph("More content in a sub-section.");
30
31    {
32        let mut table = doc.add_table(2, 3);
33        for r in 0..2 {
34            for c in 0..3 {
35                if let Some(mut cell) = table.cell(r, c) {
36                    cell.set_text(&format!("R{}C{}", r + 1, c + 1));
37                }
38            }
39        }
40    }
41
42    doc.add_paragraph("After the table.");
43
44    match doc.to_pdf() {
45        Ok(bytes) => {
46            std::fs::write("/tmp/rdocx_content.pdf", &bytes).unwrap();
47            println!(
48                "Content PDF: {} bytes -> /tmp/rdocx_content.pdf",
49                bytes.len()
50            );
51        }
52        Err(e) => println!("Content PDF failed: {e}"),
53    }
54
55    // Test 3: From feature_showcase.docx
56    let showcase_path = concat!(
57        env!("CARGO_MANIFEST_DIR"),
58        "/../../samples/feature_showcase.docx"
59    );
60    match Document::open(showcase_path) {
61        Ok(doc) => match doc.to_pdf() {
62            Ok(bytes) => {
63                std::fs::write("/tmp/rdocx_showcase.pdf", &bytes).unwrap();
64                println!(
65                    "Showcase PDF: {} bytes -> /tmp/rdocx_showcase.pdf",
66                    bytes.len()
67                );
68            }
69            Err(e) => println!("Showcase PDF failed: {e}"),
70        },
71        Err(e) => println!("Failed to open showcase: {e}"),
72    }
73}
More examples
Hide additional examples
examples/template_replace.rs (line 109)
43fn create_template(path: &Path) {
44    let mut doc = Document::new();
45    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
46    doc.set_margins(
47        Length::inches(1.0),
48        Length::inches(1.0),
49        Length::inches(1.0),
50        Length::inches(1.0),
51    );
52
53    doc.set_header("{{company_name}} — Confidential");
54    doc.set_footer("Prepared by {{author_name}} on {{date}}");
55
56    // ── Title ──
57    doc.add_paragraph("{{company_name}}")
58        .style("Heading1")
59        .alignment(Alignment::Center);
60
61    doc.add_paragraph("Project Proposal")
62        .alignment(Alignment::Center);
63
64    doc.add_paragraph("");
65
66    // ── Summary section ──
67    doc.add_paragraph("Executive Summary").style("Heading2");
68
69    doc.add_paragraph(
70        "This proposal outlines the {{project_name}} project for {{company_name}}. \
71         The primary contact is {{contact_name}} ({{contact_email}}). \
72         The proposed start date is {{start_date}} with an estimated duration of {{duration}}.",
73    );
74
75    doc.add_paragraph("");
76
77    // ── Cross-run placeholder (bold label + normal value) ──
78    doc.add_paragraph("Key Details").style("Heading2");
79
80    {
81        let mut p = doc.add_paragraph("");
82        p.add_run("Project: ").bold(true);
83        p.add_run("{{project_name}}");
84    }
85    {
86        let mut p = doc.add_paragraph("");
87        p.add_run("Budget: ").bold(true);
88        p.add_run("{{budget}}");
89    }
90    {
91        let mut p = doc.add_paragraph("");
92        p.add_run("Status: ").bold(true);
93        p.add_run("{{status}}");
94    }
95
96    doc.add_paragraph("");
97
98    // ── Table with placeholders ──
99    doc.add_paragraph("Team Members").style("Heading2");
100
101    {
102        let mut tbl = doc.add_table(4, 3);
103        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
104
105        // Header row
106        for col in 0..3 {
107            tbl.cell(0, col).unwrap().shading("2E75B6");
108        }
109        tbl.cell(0, 0).unwrap().set_text("Name");
110        tbl.cell(0, 1).unwrap().set_text("Role");
111        tbl.cell(0, 2).unwrap().set_text("Email");
112
113        tbl.cell(1, 0).unwrap().set_text("{{member1_name}}");
114        tbl.cell(1, 1).unwrap().set_text("{{member1_role}}");
115        tbl.cell(1, 2).unwrap().set_text("{{member1_email}}");
116
117        tbl.cell(2, 0).unwrap().set_text("{{member2_name}}");
118        tbl.cell(2, 1).unwrap().set_text("{{member2_role}}");
119        tbl.cell(2, 2).unwrap().set_text("{{member2_email}}");
120
121        tbl.cell(3, 0).unwrap().set_text("{{member3_name}}");
122        tbl.cell(3, 1).unwrap().set_text("{{member3_role}}");
123        tbl.cell(3, 2).unwrap().set_text("{{member3_email}}");
124    }
125
126    doc.add_paragraph("");
127
128    // ── Deliverables section ──
129    doc.add_paragraph("Deliverables").style("Heading2");
130
131    doc.add_paragraph("INSERTION_POINT");
132
133    doc.add_paragraph("");
134
135    // ── Signature block ──
136    doc.add_paragraph("Acceptance").style("Heading2");
137
138    doc.add_paragraph(
139        "By signing below, {{company_name}} agrees to the terms outlined in this proposal.",
140    );
141
142    {
143        let mut tbl = doc.add_table(2, 2);
144        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
145        tbl.cell(0, 0)
146            .unwrap()
147            .set_text("Customer: ___________________");
148        tbl.cell(0, 1)
149            .unwrap()
150            .set_text("Provider: ___________________");
151        tbl.cell(1, 0).unwrap().set_text("Date: {{date}}");
152        tbl.cell(1, 1).unwrap().set_text("Date: {{date}}");
153    }
154
155    doc.set_title("{{company_name}} — Project Proposal Template");
156    doc.set_author("Template Generator");
157
158    doc.save(path).unwrap();
159}
160
161/// Open the template, replace all placeholders, insert content, and save.
162fn fill_template(template_path: &Path, output_path: &Path) {
163    let mut doc = Document::open(template_path).unwrap();
164
165    // ── Batch replacement ──
166    let mut replacements = HashMap::new();
167    replacements.insert("{{company_name}}", "Riverside Medical Center");
168    replacements.insert("{{project_name}}", "Network Security Upgrade");
169    replacements.insert("{{contact_name}}", "Dr. Sarah Chen");
170    replacements.insert("{{contact_email}}", "s.chen@riverside.org");
171    replacements.insert("{{start_date}}", "March 1, 2026");
172    replacements.insert("{{duration}}", "12 weeks");
173    replacements.insert("{{budget}}", "$185,000");
174    replacements.insert("{{status}}", "Pending Approval");
175    replacements.insert("{{author_name}}", "James Wilson");
176    replacements.insert("{{date}}", "February 22, 2026");
177
178    // Team members
179    replacements.insert("{{member1_name}}", "James Wilson");
180    replacements.insert("{{member1_role}}", "Project Lead");
181    replacements.insert("{{member1_email}}", "j.wilson@provider.com");
182    replacements.insert("{{member2_name}}", "Maria Garcia");
183    replacements.insert("{{member2_role}}", "Security Architect");
184    replacements.insert("{{member2_email}}", "m.garcia@provider.com");
185    replacements.insert("{{member3_name}}", "David Park");
186    replacements.insert("{{member3_role}}", "Network Engineer");
187    replacements.insert("{{member3_email}}", "d.park@provider.com");
188
189    let count = doc.replace_all(&replacements);
190    println!("  Replaced {} placeholders", count);
191
192    // ── Insert deliverables at the insertion point ──
193    if let Some(idx) = doc.find_content_index("INSERTION_POINT") {
194        // Remove the placeholder paragraph
195        doc.remove_content(idx);
196
197        // Insert deliverables list
198        doc.insert_paragraph(idx, "The following deliverables are included:");
199
200        // Insert a deliverables table
201        let mut tbl = doc.insert_table(idx + 1, 5, 3);
202        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
203
204        for col in 0..3 {
205            tbl.cell(0, col).unwrap().shading("E2EFDA");
206        }
207        tbl.cell(0, 0).unwrap().set_text("Phase");
208        tbl.cell(0, 1).unwrap().set_text("Description");
209        tbl.cell(0, 2).unwrap().set_text("Timeline");
210
211        tbl.cell(1, 0).unwrap().set_text("1. Discovery");
212        tbl.cell(1, 1)
213            .unwrap()
214            .set_text("Network assessment and asset inventory");
215        tbl.cell(1, 2).unwrap().set_text("Weeks 1-3");
216
217        tbl.cell(2, 0).unwrap().set_text("2. Design");
218        tbl.cell(2, 1)
219            .unwrap()
220            .set_text("Security architecture and policy design");
221        tbl.cell(2, 2).unwrap().set_text("Weeks 4-6");
222
223        tbl.cell(3, 0).unwrap().set_text("3. Implementation");
224        tbl.cell(3, 1)
225            .unwrap()
226            .set_text("Deploy monitoring and access controls");
227        tbl.cell(3, 2).unwrap().set_text("Weeks 7-10");
228
229        tbl.cell(4, 0).unwrap().set_text("4. Validation");
230        tbl.cell(4, 1)
231            .unwrap()
232            .set_text("Testing, training, and handover");
233        tbl.cell(4, 2).unwrap().set_text("Weeks 11-12");
234
235        println!("  Inserted deliverables table at position {}", idx);
236    }
237
238    // ── Update metadata ──
239    doc.set_title("Riverside Medical Center — Network Security Upgrade Proposal");
240    doc.set_author("James Wilson");
241    doc.set_subject("Project Proposal");
242    doc.set_keywords("security, network, medical, proposal");
243
244    doc.save(output_path).unwrap();
245}
examples/styled_tables.rs (line 54)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
examples/generate_samples.rs (line 306)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 359)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
684
685// =============================================================================
686// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
687// =============================================================================
688fn generate_proposal(_samples_dir: &Path) -> Document {
689    let mut doc = Document::new();
690
691    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
692    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
693    doc.set_margins(
694        Length::inches(1.0),
695        Length::inches(1.0),
696        Length::inches(1.0),
697        Length::inches(1.0),
698    );
699    doc.set_title("AI Platform Modernization Proposal");
700    doc.set_author("Walter White");
701    doc.set_subject("Technology Proposal");
702    doc.set_keywords("proposal, AI, modernization, Tensorbee");
703
704    // Banner header
705    let logo_img = create_logo_png(220, 48);
706    let banner = build_header_banner_xml(
707        "rId1",
708        &BannerOpts {
709            bg_color: "1B2A4A",
710            banner_width: 7772400,
711            banner_height: 914400,
712            logo_width: 2011680,
713            logo_height: 438912,
714            logo_x_offset: 295125,
715            logo_y_offset: 237744,
716        },
717    );
718    doc.set_raw_header_with_images(
719        banner,
720        &[("rId1", &logo_img, "logo.png")],
721        rdocx_oxml::header_footer::HdrFtrType::Default,
722    );
723    doc.set_different_first_page(true);
724    let cover_banner = build_header_banner_xml(
725        "rId1",
726        &BannerOpts {
727            bg_color: "C5922E",
728            banner_width: 7772400,
729            banner_height: 914400,
730            logo_width: 2011680,
731            logo_height: 438912,
732            logo_x_offset: 295125,
733            logo_y_offset: 237744,
734        },
735    );
736    doc.set_raw_header_with_images(
737        cover_banner,
738        &[("rId1", &logo_img, "logo.png")],
739        rdocx_oxml::header_footer::HdrFtrType::First,
740    );
741    doc.set_margins(
742        Length::twips(2292),
743        Length::twips(1440),
744        Length::twips(1440),
745        Length::twips(1440),
746    );
747    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
748    doc.set_footer("Tensorbee — Confidential");
749
750    // Custom styles
751    doc.add_style(
752        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
753            .based_on("Normal")
754            .run_properties({
755                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
756                rpr.bold = Some(true);
757                rpr.font_ascii = Some("Georgia".to_string());
758                rpr.font_hansi = Some("Georgia".to_string());
759                rpr.sz = Some(rdocx_oxml::HalfPoint(56)); // half-points → 28pt
760                rpr.color = Some("1B2A4A".to_string());
761                rpr
762            }),
763    );
764
765    // ── Cover Page ──
766    for _ in 0..4 {
767        doc.add_paragraph("");
768    }
769    doc.add_paragraph("AI Platform Modernization")
770        .style("ProposalTitle")
771        .alignment(Alignment::Center);
772    doc.add_paragraph("")
773        .alignment(Alignment::Center)
774        .border_bottom(BorderStyle::Single, 8, "C5922E");
775    {
776        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
777        p.add_run("Prepared for Global Financial Corp.")
778            .size(14.0)
779            .color("666666")
780            .italic(true);
781    }
782    {
783        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
784        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
785            .size(12.0)
786            .color("888888");
787    }
788    {
789        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
790        p.add_run("February 22, 2026").size(12.0).color("888888");
791    }
792
793    // ── TOC ──
794    doc.add_paragraph("").page_break_before(true);
795    doc.add_paragraph("Table of Contents").style("Heading1");
796    // We'll add a manual-style TOC since the insert_toc goes at a specific position
797    doc.insert_toc(doc.content_count(), 2);
798
799    // ── Executive Summary ──
800    doc.add_paragraph("").page_break_before(true);
801    doc.add_paragraph("Executive Summary").style("Heading1");
802    doc.add_paragraph(
803        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
804         transitioning from legacy batch-processing systems to a real-time inference architecture. \
805         This transformation will reduce model deployment time from weeks to hours, improve \
806         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
807    )
808    .first_line_indent(Length::inches(0.3));
809    doc.add_paragraph(
810        "The project spans three phases over 18 months, with a total investment of $2.8M. \
811         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
812         similar transformations for three Fortune 500 financial institutions.",
813    )
814    .first_line_indent(Length::inches(0.3));
815
816    // Key metrics table
817    doc.add_paragraph("");
818    {
819        let mut tbl = doc
820            .add_table(5, 2)
821            .borders(BorderStyle::Single, 4, "1B2A4A")
822            .width_pct(80.0);
823        tbl.cell(0, 0).unwrap().set_text("Key Metric");
824        tbl.cell(0, 0).unwrap().shading("1B2A4A");
825        tbl.cell(0, 1).unwrap().set_text("Value");
826        tbl.cell(0, 1).unwrap().shading("1B2A4A");
827        let rows = [
828            ("Total Investment", "$2.8M"),
829            ("Annual Savings", "$4.2M"),
830            ("ROI (Year 1)", "150%"),
831            ("Timeline", "18 months"),
832        ];
833        for (i, (k, v)) in rows.iter().enumerate() {
834            tbl.cell(i + 1, 0).unwrap().set_text(k);
835            if i % 2 == 0 {
836                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
837            }
838            tbl.cell(i + 1, 1).unwrap().set_text(v);
839            if i % 2 == 0 {
840                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
841            }
842        }
843    }
844
845    // ── Problem Statement ──
846    doc.add_paragraph("").page_break_before(true);
847    doc.add_paragraph("Problem Statement").style("Heading1");
848    doc.add_paragraph(
849        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
850    );
851    doc.add_numbered_list_item("Deployment Latency — New models take 3-4 weeks to deploy to production, compared to the industry benchmark of 2-3 days.", 0);
852    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
853    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
854
855    // ── Proposed Solution ──
856    doc.add_paragraph("").page_break_before(true);
857    doc.add_paragraph("Proposed Solution").style("Heading1");
858
859    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
860        .style("Heading2");
861    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
862    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
863    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
864
865    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
866        .style("Heading2");
867    doc.add_bullet_list_item(
868        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
869        0,
870    );
871    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
872    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
873
874    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
875        .style("Heading2");
876    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
877    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
878    doc.add_bullet_list_item(
879        "Self-service model deployment portal for data science teams",
880        0,
881    );
882
883    // ── Budget ──
884    doc.add_paragraph("Budget Breakdown").style("Heading1");
885    {
886        let mut tbl = doc
887            .add_table(6, 3)
888            .borders(BorderStyle::Single, 4, "1B2A4A")
889            .width_pct(100.0);
890        tbl.cell(0, 0).unwrap().set_text("Category");
891        tbl.cell(0, 0).unwrap().shading("1B2A4A");
892        tbl.cell(0, 1).unwrap().set_text("Cost");
893        tbl.cell(0, 1).unwrap().shading("1B2A4A");
894        tbl.cell(0, 2).unwrap().set_text("Notes");
895        tbl.cell(0, 2).unwrap().shading("1B2A4A");
896        let items = [
897            (
898                "Infrastructure",
899                "$450,000",
900                "Cloud compute + GPU instances",
901            ),
902            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
903            (
904                "Software Licenses",
905                "$350,000",
906                "Kafka, monitoring, CI/CD tools",
907            ),
908            (
909                "Training & Enablement",
910                "$200,000",
911                "Team training, documentation",
912            ),
913            ("Contingency (10%)", "$200,000", "Risk buffer"),
914        ];
915        for (i, (cat, cost, note)) in items.iter().enumerate() {
916            tbl.cell(i + 1, 0).unwrap().set_text(cat);
917            tbl.cell(i + 1, 1).unwrap().set_text(cost);
918            tbl.cell(i + 1, 2).unwrap().set_text(note);
919            if i % 2 == 0 {
920                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
921                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
922                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
923            }
924        }
925    }
926
927    // ── Closing ──
928    doc.add_paragraph("");
929    doc.add_paragraph("Next Steps").style("Heading1");
930    doc.add_numbered_list_item(
931        "Schedule technical deep-dive with GFC engineering team (Week 1)",
932        0,
933    );
934    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
935    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
936
937    doc.add_paragraph("");
938    {
939        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
940        p.add_run("Walter White")
941            .bold(true)
942            .size(14.0)
943            .color("1B2A4A");
944    }
945    {
946        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
947        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
948            .size(10.0)
949            .color("888888");
950    }
951
952    doc
953}
954
955// =============================================================================
956// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
957// =============================================================================
958fn generate_quote(_samples_dir: &Path) -> Document {
959    let mut doc = Document::new();
960
961    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
962    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
963    doc.set_margins(
964        Length::inches(0.75),
965        Length::inches(0.75),
966        Length::inches(0.75),
967        Length::inches(0.75),
968    );
969    doc.set_title("Quotation QT-2026-0147");
970    doc.set_author("Walter White");
971    doc.set_keywords("quote, BOM, Tensorbee");
972
973    doc.set_header("Tensorbee — Quotation");
974    doc.set_footer("QT-2026-0147 | Page");
975
976    // ── Header Block ──
977    {
978        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
979        p.add_run("TENSORBEE")
980            .bold(true)
981            .size(28.0)
982            .color("008B8B")
983            .font("Helvetica");
984    }
985    doc.add_paragraph("123 Innovation Drive, Suite 400")
986        .alignment(Alignment::Left);
987    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
988        .alignment(Alignment::Left);
989    doc.add_paragraph("")
990        .border_bottom(BorderStyle::Single, 8, "008B8B");
991
992    // Quote meta
993    doc.add_paragraph("");
994    {
995        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
996        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
997        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
998        tbl.cell(0, 2).unwrap().set_text("DATE");
999        tbl.cell(0, 2).unwrap().shading("008B8B");
1000        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1001        tbl.cell(0, 3).unwrap().shading("008B8B");
1002        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1003        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1004        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1005        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1006        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1007        tbl.cell(2, 1).unwrap().set_text("Walter White");
1008        tbl.cell(2, 2).unwrap().set_text("Payment:");
1009        tbl.cell(2, 3).unwrap().set_text("Net 30");
1010        tbl.cell(3, 0).unwrap().set_text("Customer:");
1011        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1012        tbl.cell(3, 2).unwrap().set_text("Currency:");
1013        tbl.cell(3, 3).unwrap().set_text("USD");
1014    }
1015
1016    doc.add_paragraph("");
1017
1018    // ── Bill of Materials ──
1019    {
1020        let mut p = doc.add_paragraph("");
1021        p.add_run("Bill of Materials")
1022            .bold(true)
1023            .size(16.0)
1024            .color("1A3C3C");
1025    }
1026    doc.add_paragraph("");
1027
1028    {
1029        let mut tbl = doc
1030            .add_table(10, 6)
1031            .borders(BorderStyle::Single, 2, "008B8B")
1032            .width_pct(100.0)
1033            .layout_fixed();
1034        // Headers
1035        let hdrs = [
1036            "#",
1037            "Part Number",
1038            "Description",
1039            "Qty",
1040            "Unit Price",
1041            "Total",
1042        ];
1043        for (c, h) in hdrs.iter().enumerate() {
1044            tbl.cell(0, c).unwrap().set_text(h);
1045            tbl.cell(0, c).unwrap().shading("008B8B");
1046        }
1047
1048        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1049            (
1050                "TB-GPU-A100",
1051                "NVIDIA A100 80GB GPU",
1052                "4",
1053                "$12,500.00",
1054                "$50,000.00",
1055            ),
1056            (
1057                "TB-SRV-R750",
1058                "Dell R750xa Server Chassis",
1059                "2",
1060                "$8,200.00",
1061                "$16,400.00",
1062            ),
1063            (
1064                "TB-RAM-256G",
1065                "256GB DDR5 ECC Memory Module",
1066                "8",
1067                "$890.00",
1068                "$7,120.00",
1069            ),
1070            (
1071                "TB-SSD-3840",
1072                "3.84TB NVMe U.2 SSD",
1073                "8",
1074                "$1,150.00",
1075                "$9,200.00",
1076            ),
1077            (
1078                "TB-NET-CX7",
1079                "ConnectX-7 200GbE NIC",
1080                "4",
1081                "$1,800.00",
1082                "$7,200.00",
1083            ),
1084            (
1085                "TB-SW-48P",
1086                "48-Port 100GbE Switch",
1087                "1",
1088                "$22,000.00",
1089                "$22,000.00",
1090            ),
1091            (
1092                "TB-CAB-RACK",
1093                "42U Server Rack + PDU",
1094                "1",
1095                "$4,500.00",
1096                "$4,500.00",
1097            ),
1098            (
1099                "TB-SVC-INST",
1100                "Installation & Configuration",
1101                "1",
1102                "$8,500.00",
1103                "$8,500.00",
1104            ),
1105            (
1106                "TB-SVC-SUPP",
1107                "3-Year Premium Support",
1108                "1",
1109                "$15,000.00",
1110                "$15,000.00",
1111            ),
1112        ];
1113
1114        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1115            let row = i + 1;
1116            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1117            tbl.cell(row, 1).unwrap().set_text(pn);
1118            tbl.cell(row, 2).unwrap().set_text(desc);
1119            tbl.cell(row, 3).unwrap().set_text(qty);
1120            tbl.cell(row, 4).unwrap().set_text(unit);
1121            tbl.cell(row, 5).unwrap().set_text(total);
1122            if i % 2 == 0 {
1123                for c in 0..6 {
1124                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1125                }
1126            }
1127        }
1128    }
1129
1130    doc.add_paragraph("");
1131
1132    // ── Totals ──
1133    {
1134        let mut tbl = doc
1135            .add_table(4, 2)
1136            .borders(BorderStyle::Single, 2, "008B8B")
1137            .width(Length::inches(3.5))
1138            .alignment(Alignment::Right);
1139        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1140        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1141        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1142        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1143        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1144        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1145        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1146        tbl.cell(3, 0).unwrap().shading("E07020");
1147        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1148        tbl.cell(3, 1).unwrap().shading("E07020");
1149    }
1150
1151    doc.add_paragraph("");
1152
1153    // ── Terms & Conditions ──
1154    {
1155        let mut p = doc.add_paragraph("");
1156        p.add_run("Terms & Conditions")
1157            .bold(true)
1158            .size(14.0)
1159            .color("1A3C3C");
1160    }
1161    doc.add_numbered_list_item(
1162        "This quotation is valid for 30 calendar days from the date of issue.",
1163        0,
1164    );
1165    doc.add_numbered_list_item(
1166        "All prices are in USD and exclusive of applicable taxes unless stated.",
1167        0,
1168    );
1169    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1170    doc.add_numbered_list_item(
1171        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1172        0,
1173    );
1174    doc.add_numbered_list_item(
1175        "Warranty: 3-year manufacturer warranty on all hardware components.",
1176        0,
1177    );
1178    doc.add_numbered_list_item(
1179        "Returns subject to 15% restocking fee if initiated after 14 days.",
1180        0,
1181    );
1182
1183    doc.add_paragraph("");
1184    doc.add_paragraph("")
1185        .border_bottom(BorderStyle::Single, 4, "008B8B");
1186    doc.add_paragraph("");
1187    {
1188        let mut p = doc.add_paragraph("");
1189        p.add_run("Accepted by: ").bold(true);
1190        p.add_run("___________________________ Date: ___________");
1191    }
1192    {
1193        let mut p = doc.add_paragraph("");
1194        p.add_run("Print Name: ").bold(true);
1195        p.add_run("___________________________ Title: ___________");
1196    }
1197
1198    doc
1199}
1200
1201// =============================================================================
1202// 4. INVOICE — Crimson + charcoal scheme
1203// =============================================================================
1204fn generate_invoice(_samples_dir: &Path) -> Document {
1205    let mut doc = Document::new();
1206
1207    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1208    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1209    doc.set_margins(
1210        Length::inches(0.75),
1211        Length::inches(0.75),
1212        Length::inches(0.75),
1213        Length::inches(0.75),
1214    );
1215    doc.set_title("Invoice INV-2026-0392");
1216    doc.set_author("Walter White");
1217    doc.set_keywords("invoice, Tensorbee");
1218
1219    doc.set_footer("Tensorbee — Thank you for your business!");
1220
1221    // ── Company & Invoice Header ──
1222    {
1223        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1224        // Company name left, INVOICE right
1225        {
1226            let mut cell = tbl.cell(0, 0).unwrap();
1227            let mut p = cell.add_paragraph("");
1228            p.add_run("TENSORBEE")
1229                .bold(true)
1230                .size(32.0)
1231                .color("B22222")
1232                .font("Helvetica");
1233        }
1234        {
1235            let mut cell = tbl.cell(0, 1).unwrap();
1236            let mut p = cell.add_paragraph("");
1237            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1238        }
1239        tbl.cell(1, 0)
1240            .unwrap()
1241            .set_text("123 Innovation Drive, Suite 400");
1242        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1243        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1244        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1245        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1246        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1247    }
1248
1249    doc.add_paragraph("")
1250        .border_bottom(BorderStyle::Thick, 8, "B22222");
1251    doc.add_paragraph("");
1252
1253    // ── Bill To / Ship To ──
1254    {
1255        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1256        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1257        tbl.cell(0, 0).unwrap().shading("B22222");
1258        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1259        tbl.cell(0, 1).unwrap().shading("B22222");
1260        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1261        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1262        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1263        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1264        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1265        tbl.cell(3, 1)
1266            .unwrap()
1267            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1268    }
1269
1270    doc.add_paragraph("");
1271
1272    // ── Line Items ──
1273    {
1274        let mut tbl = doc
1275            .add_table(8, 5)
1276            .borders(BorderStyle::Single, 2, "B22222")
1277            .width_pct(100.0);
1278        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1279        for (c, h) in hdrs.iter().enumerate() {
1280            tbl.cell(0, c).unwrap().set_text(h);
1281            tbl.cell(0, c).unwrap().shading("B22222");
1282        }
1283        let items = [
1284            (
1285                "ML Infrastructure Setup — Phase 1",
1286                "1",
1287                "$45,000.00",
1288                "$3,881.25",
1289                "$48,881.25",
1290            ),
1291            (
1292                "Data Pipeline Development (160 hrs)",
1293                "160",
1294                "$225.00",
1295                "$3,105.00",
1296                "$39,105.00",
1297            ),
1298            (
1299                "GPU Cluster Configuration",
1300                "1",
1301                "$12,000.00",
1302                "$1,035.00",
1303                "$13,035.00",
1304            ),
1305            (
1306                "API Gateway Implementation",
1307                "1",
1308                "$18,500.00",
1309                "$1,595.63",
1310                "$20,095.63",
1311            ),
1312            (
1313                "Load Testing & QA (80 hrs)",
1314                "80",
1315                "$195.00",
1316                "$1,345.50",
1317                "$16,945.50",
1318            ),
1319            (
1320                "Documentation & Training",
1321                "1",
1322                "$8,000.00",
1323                "$690.00",
1324                "$8,690.00",
1325            ),
1326            (
1327                "Project Management (3 months)",
1328                "3",
1329                "$6,500.00",
1330                "$1,679.63",
1331                "$21,179.63",
1332            ),
1333        ];
1334        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1335            let r = i + 1;
1336            tbl.cell(r, 0).unwrap().set_text(desc);
1337            tbl.cell(r, 1).unwrap().set_text(qty);
1338            tbl.cell(r, 2).unwrap().set_text(unit);
1339            tbl.cell(r, 3).unwrap().set_text(tax);
1340            tbl.cell(r, 4).unwrap().set_text(amt);
1341            if i % 2 == 0 {
1342                for c in 0..5 {
1343                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1344                }
1345            }
1346        }
1347    }
1348
1349    doc.add_paragraph("");
1350
1351    // ── Totals ──
1352    {
1353        let mut tbl = doc
1354            .add_table(4, 2)
1355            .borders(BorderStyle::Single, 2, "B22222")
1356            .width(Length::inches(3.0))
1357            .alignment(Alignment::Right);
1358        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1359        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1360        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1361        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1362        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1363        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1364        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1365        tbl.cell(3, 0).unwrap().shading("B22222");
1366        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1367        tbl.cell(3, 1).unwrap().shading("B22222");
1368    }
1369
1370    doc.add_paragraph("");
1371    doc.add_paragraph("");
1372
1373    // ── Payment Details ──
1374    {
1375        let mut p = doc.add_paragraph("");
1376        p.add_run("Payment Details")
1377            .bold(true)
1378            .size(14.0)
1379            .color("333333");
1380    }
1381    doc.add_paragraph("Bank: Silicon Valley Bank")
1382        .indent_left(Length::inches(0.3));
1383    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1384        .indent_left(Length::inches(0.3));
1385    doc.add_paragraph("Routing: 121140399")
1386        .indent_left(Length::inches(0.3));
1387    doc.add_paragraph("Swift: SVBKUS6S")
1388        .indent_left(Length::inches(0.3));
1389
1390    doc.add_paragraph("");
1391
1392    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1393        .shading("FAF0F0")
1394        .border_all(BorderStyle::Single, 2, "B22222");
1395
1396    doc
1397}
1398
1399// =============================================================================
1400// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1401// =============================================================================
1402fn generate_report(_samples_dir: &Path) -> Document {
1403    let mut doc = Document::new();
1404
1405    // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1406    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1407    doc.set_margins(
1408        Length::inches(1.0),
1409        Length::inches(1.0),
1410        Length::inches(1.0),
1411        Length::inches(1.0),
1412    );
1413    doc.set_title("Q4 2025 Environmental Impact Report");
1414    doc.set_author("Walter White");
1415    doc.set_subject("Quarterly Environmental Report");
1416    doc.set_keywords("environment, sustainability, report, Tensorbee");
1417
1418    doc.set_different_first_page(true);
1419    doc.set_first_page_header("");
1420    doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1421    doc.set_footer("CONFIDENTIAL — Page");
1422
1423    // ── Cover ──
1424    let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1425    doc.add_background_image(&cover_bg, "report_cover.png");
1426
1427    for _ in 0..5 {
1428        doc.add_paragraph("");
1429    }
1430    {
1431        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1432        p.add_run("Q4 2025")
1433            .bold(true)
1434            .size(48.0)
1435            .color("FFFFFF")
1436            .font("Georgia");
1437    }
1438    {
1439        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440        p.add_run("Environmental Impact Report")
1441            .size(24.0)
1442            .color("90EE90")
1443            .font("Georgia")
1444            .italic(true);
1445    }
1446    doc.add_paragraph("");
1447    {
1448        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1449        p.add_run("Tensorbee — Sustainability Division")
1450            .size(14.0)
1451            .color("C0C0C0");
1452    }
1453    {
1454        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1455        p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1456            .size(11.0)
1457            .color("AAAAAA");
1458    }
1459
1460    // ── TOC ──
1461    doc.add_paragraph("").page_break_before(true);
1462    doc.insert_toc(doc.content_count(), 3);
1463
1464    // ── Section 1: Executive Overview ──
1465    doc.add_paragraph("").page_break_before(true);
1466    doc.add_paragraph("Executive Overview").style("Heading1");
1467    doc.add_paragraph(
1468        "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1469         sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1470         to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1471         100% renewable energy in our primary data centers and the launch of our carbon offset \
1472         marketplace.",
1473    )
1474    .first_line_indent(Length::inches(0.3));
1475
1476    // Image: performance chart
1477    let chart_img = create_chart_png(400, 200);
1478    doc.add_paragraph("");
1479    doc.add_picture(
1480        &chart_img,
1481        "performance_chart.png",
1482        Length::inches(5.0),
1483        Length::inches(2.5),
1484    )
1485    .alignment(Alignment::Center);
1486    {
1487        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1488        p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1489            .italic(true)
1490            .size(9.0)
1491            .color("666666");
1492    }
1493
1494    // ── Section 2: Energy Consumption ──
1495    doc.add_paragraph("").page_break_before(true);
1496    doc.add_paragraph("Energy Consumption").style("Heading1");
1497
1498    doc.add_paragraph("Data Center Operations")
1499        .style("Heading2");
1500    doc.add_paragraph(
1501        "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1502         a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1503         server consolidation.",
1504    );
1505
1506    // Data table
1507    {
1508        let mut tbl = doc
1509            .add_table(5, 4)
1510            .borders(BorderStyle::Single, 4, "2D5016")
1511            .width_pct(100.0);
1512        let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1513        for (c, h) in hdrs.iter().enumerate() {
1514            tbl.cell(0, c).unwrap().set_text(h);
1515            tbl.cell(0, c).unwrap().shading("2D5016");
1516        }
1517        let rows = [
1518            ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1519            ("Dublin (EU)", "2.1", "1.2", "1.18"),
1520            ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1521            ("TOTAL", "7.1", "4.2", "1.17 avg"),
1522        ];
1523        for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1524            tbl.cell(i + 1, 0).unwrap().set_text(dc);
1525            tbl.cell(i + 1, 1).unwrap().set_text(cap);
1526            tbl.cell(i + 1, 2).unwrap().set_text(use_);
1527            tbl.cell(i + 1, 3).unwrap().set_text(pue);
1528            if i == 3 {
1529                for c in 0..4 {
1530                    tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1531                }
1532            }
1533        }
1534    }
1535
1536    doc.add_paragraph("");
1537
1538    doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1539    doc.add_paragraph("Breakdown of energy sources across all facilities:");
1540
1541    doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1542    doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1543    doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1544    doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1545
1546    // ── Section 3: Water & Waste ──
1547    doc.add_paragraph("Water & Waste Management")
1548        .style("Heading1");
1549
1550    doc.add_paragraph("Water Usage").style("Heading2");
1551    doc.add_paragraph(
1552        "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1553         Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1554    );
1555
1556    doc.add_paragraph("Waste Reduction").style("Heading2");
1557    doc.add_paragraph("E-waste management results for Q4:")
1558        .keep_with_next(true);
1559    {
1560        let mut tbl = doc
1561            .add_table(4, 3)
1562            .borders(BorderStyle::Single, 2, "6B8E23")
1563            .width_pct(80.0);
1564        tbl.cell(0, 0).unwrap().set_text("Category");
1565        tbl.cell(0, 0).unwrap().shading("6B8E23");
1566        tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1567        tbl.cell(0, 1).unwrap().shading("6B8E23");
1568        tbl.cell(0, 2).unwrap().set_text("Recycled %");
1569        tbl.cell(0, 2).unwrap().shading("6B8E23");
1570        let rows = [
1571            ("Server Hardware", "2,450", "94%"),
1572            ("Networking Equipment", "820", "91%"),
1573            ("Storage Media", "340", "99%"),
1574        ];
1575        for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1576            tbl.cell(i + 1, 0).unwrap().set_text(cat);
1577            tbl.cell(i + 1, 1).unwrap().set_text(wt);
1578            tbl.cell(i + 1, 2).unwrap().set_text(pct);
1579        }
1580    }
1581
1582    // ── Section 4: Initiatives ──
1583    doc.add_paragraph("").page_break_before(true);
1584    doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1585
1586    doc.add_paragraph("Planned Programs").style("Heading2");
1587    doc.add_numbered_list_item(
1588        "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1589        0,
1590    );
1591    doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1592    doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1593    doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1594
1595    doc.add_paragraph("Investment Targets").style("Heading2");
1596    doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1597        .keep_with_next(true);
1598    {
1599        let mut tbl = doc
1600            .add_table(5, 2)
1601            .borders(BorderStyle::Single, 4, "2D5016");
1602        tbl.cell(0, 0).unwrap().set_text("Initiative");
1603        tbl.cell(0, 0).unwrap().shading("2D5016");
1604        tbl.cell(0, 1).unwrap().set_text("Budget");
1605        tbl.cell(0, 1).unwrap().shading("2D5016");
1606        let rows = [
1607            ("Battery Storage", "$1.2M"),
1608            ("Immersion Cooling Pilot", "$800K"),
1609            ("Solar Panel Expansion", "$2.1M"),
1610            ("Carbon Credits", "$500K"),
1611        ];
1612        for (i, (init, budget)) in rows.iter().enumerate() {
1613            tbl.cell(i + 1, 0).unwrap().set_text(init);
1614            tbl.cell(i + 1, 1).unwrap().set_text(budget);
1615            if i % 2 == 0 {
1616                tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1617                tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1618            }
1619        }
1620    }
1621
1622    // Image: sustainability roadmap
1623    let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1624    doc.add_paragraph("");
1625    doc.add_picture(
1626        &roadmap_img,
1627        "roadmap.png",
1628        Length::inches(6.0),
1629        Length::inches(1.2),
1630    )
1631    .alignment(Alignment::Center);
1632    {
1633        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1634        p.add_run("Figure 2: 2026 Sustainability Roadmap")
1635            .italic(true)
1636            .size(9.0)
1637            .color("666666");
1638    }
1639
1640    doc.add_paragraph("");
1641    doc.add_paragraph(
1642        "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1643    )
1644    .shading("FFFAF0")
1645    .border_all(BorderStyle::Single, 2, "2D5016");
1646
1647    doc
1648}
1649
1650// =============================================================================
1651// 6. LETTER — Slate blue + silver scheme
1652// =============================================================================
1653fn generate_letter(_samples_dir: &Path) -> Document {
1654    let mut doc = Document::new();
1655
1656    // Colors: Slate #4A5568, Blue accent #3182CE
1657    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1658    doc.set_margins(
1659        Length::inches(1.25),
1660        Length::inches(1.25),
1661        Length::inches(1.25),
1662        Length::inches(1.25),
1663    );
1664    doc.set_title("Business Letter");
1665    doc.set_author("Walter White");
1666
1667    // Banner header using raw XML
1668    let logo_img = create_logo_png(220, 48);
1669    let banner = build_header_banner_xml(
1670        "rId1",
1671        &BannerOpts {
1672            bg_color: "4A5568",
1673            banner_width: 7772400,
1674            banner_height: 731520, // ~0.8"
1675            logo_width: 2011680,
1676            logo_height: 438912,
1677            logo_x_offset: 295125,
1678            logo_y_offset: 146304,
1679        },
1680    );
1681    doc.set_raw_header_with_images(
1682        banner,
1683        &[("rId1", &logo_img, "logo.png")],
1684        rdocx_oxml::header_footer::HdrFtrType::Default,
1685    );
1686    doc.set_margins(
1687        Length::twips(2000),
1688        Length::twips(1800),
1689        Length::twips(1440),
1690        Length::twips(1800),
1691    );
1692    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
1693
1694    // Footer with contact
1695    doc.set_footer(
1696        "Tensorbee | 123 Innovation Drive, Suite 400 | San Francisco, CA 94105 | tensorbee.com",
1697    );
1698
1699    // ── Sender Address ──
1700    doc.add_paragraph("Walter White");
1701    doc.add_paragraph("Chief Executive Officer");
1702    doc.add_paragraph("Tensorbee");
1703    doc.add_paragraph("123 Innovation Drive, Suite 400");
1704    doc.add_paragraph("San Francisco, CA 94105");
1705    doc.add_paragraph("");
1706    doc.add_paragraph("February 22, 2026");
1707
1708    doc.add_paragraph("");
1709
1710    // ── Recipient ──
1711    doc.add_paragraph("Ms. Sarah Chen");
1712    doc.add_paragraph("VP of Engineering");
1713    doc.add_paragraph("Meridian Dynamics LLC");
1714    doc.add_paragraph("456 Enterprise Boulevard");
1715    doc.add_paragraph("Austin, TX 78701");
1716
1717    doc.add_paragraph("");
1718
1719    // ── Subject ──
1720    {
1721        let mut p = doc.add_paragraph("");
1722        p.add_run("Re: Strategic Technology Partnership — Phase 2 Expansion")
1723            .bold(true);
1724    }
1725
1726    doc.add_paragraph("");
1727
1728    // ── Body ──
1729    doc.add_paragraph("Dear Ms. Chen,");
1730    doc.add_paragraph("");
1731
1732    doc.add_paragraph(
1733        "I am writing to express Tensorbee's enthusiasm for expanding our strategic technology \
1734         partnership with Meridian Dynamics. Following the successful completion of Phase 1, \
1735         which delivered a 40% improvement in your ML inference pipeline throughput, we believe \
1736         the foundation is firmly established for an ambitious Phase 2 engagement.",
1737    )
1738    .first_line_indent(Length::inches(0.5));
1739
1740    doc.add_paragraph(
1741        "During our recent executive review, your team highlighted three priority areas for \
1742         the next phase of collaboration:",
1743    )
1744    .first_line_indent(Length::inches(0.5));
1745
1746    doc.add_numbered_list_item(
1747        "Real-time anomaly detection for your financial transaction monitoring system, \
1748         targeting sub-100ms latency at 50,000 transactions per second.",
1749        0,
1750    );
1751    doc.add_numbered_list_item(
1752        "Federated learning infrastructure to enable model training across your distributed \
1753         data centers without centralizing sensitive financial data.",
1754        0,
1755    );
1756    doc.add_numbered_list_item(
1757        "MLOps automation to reduce your model deployment cycle from the current 5 days \
1758         to under 4 hours.",
1759        0,
1760    );
1761
1762    doc.add_paragraph(
1763        "Our engineering team has prepared a detailed technical proposal addressing each of \
1764         these areas. We have allocated a dedicated team of eight senior engineers, led by \
1765         our CTO, to ensure continuity with the Phase 1 team your organization has already \
1766         built a productive working relationship with.",
1767    )
1768    .first_line_indent(Length::inches(0.5));
1769
1770    doc.add_paragraph(
1771        "I would welcome the opportunity to present our Phase 2 proposal in person. My \
1772         assistant will reach out to coordinate a meeting at your convenience during the \
1773         first week of March.",
1774    )
1775    .first_line_indent(Length::inches(0.5));
1776
1777    doc.add_paragraph("");
1778
1779    doc.add_paragraph("Warm regards,");
1780    doc.add_paragraph("");
1781    doc.add_paragraph("");
1782
1783    // Signature line
1784    doc.add_paragraph("")
1785        .border_bottom(BorderStyle::Single, 4, "4A5568");
1786    {
1787        let mut p = doc.add_paragraph("");
1788        p.add_run("Walter White").bold(true).size(12.0);
1789    }
1790    {
1791        let mut p = doc.add_paragraph("");
1792        p.add_run("Chief Executive Officer, Tensorbee")
1793            .italic(true)
1794            .size(10.0)
1795            .color("666666");
1796    }
1797    {
1798        let mut p = doc.add_paragraph("");
1799        p.add_run("walter@tensorbee.com | +1 (415) 555-0199")
1800            .size(10.0)
1801            .color("888888");
1802    }
1803
1804    doc
1805}
1806
1807// =============================================================================
1808// 7. EMPLOYMENT CONTRACT — Purple + charcoal scheme
1809// =============================================================================
1810fn generate_contract(_samples_dir: &Path) -> Document {
1811    let mut doc = Document::new();
1812
1813    // Colors: Purple #5B2C6F, Plum #8E44AD, Gray #2C3E50
1814    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1815    doc.set_margins(
1816        Length::inches(1.25),
1817        Length::inches(1.0),
1818        Length::inches(1.0),
1819        Length::inches(1.0),
1820    );
1821    doc.set_title("Employment Agreement");
1822    doc.set_author("Tensorbee HR Department");
1823    doc.set_subject("Employment Contract — Walter White");
1824    doc.set_keywords("employment, contract, agreement, Tensorbee");
1825
1826    doc.set_header("TENSORBEE — Employment Agreement");
1827    doc.set_footer("Employment Agreement — Walter White — Page");
1828
1829    // ── Title Block ──
1830    doc.add_paragraph("")
1831        .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1832    {
1833        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1834        p.add_run("EMPLOYMENT AGREEMENT")
1835            .bold(true)
1836            .size(24.0)
1837            .color("5B2C6F")
1838            .font("Georgia");
1839    }
1840    doc.add_paragraph("")
1841        .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1842    doc.add_paragraph("");
1843
1844    // ── Parties ──
1845    doc.add_paragraph(
1846        "This Employment Agreement (\"Agreement\") is entered into as of February 22, 2026 \
1847         (\"Effective Date\"), by and between:",
1848    );
1849
1850    doc.add_paragraph("");
1851
1852    {
1853        let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1854        p.add_run("EMPLOYER: ").bold(true);
1855        p.add_run(
1856            "Tensorbee, Inc., a Delaware corporation with its principal place of business at \
1857                   123 Innovation Drive, Suite 400, San Francisco, CA 94105 (\"Company\")",
1858        );
1859    }
1860    doc.add_paragraph("");
1861    {
1862        let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1863        p.add_run("EMPLOYEE: ").bold(true);
1864        p.add_run(
1865            "Walter White, residing at 308 Negra Arroyo Lane, Albuquerque, NM 87104 (\"Employee\")",
1866        );
1867    }
1868
1869    doc.add_paragraph("");
1870    doc.add_paragraph("The Company and Employee are collectively referred to as the \"Parties.\"");
1871    doc.add_paragraph("");
1872
1873    // ── Article 1: Position and Duties ──
1874    doc.add_paragraph("Article 1 — Position and Duties")
1875        .style("Heading1");
1876    {
1877        let mut p = doc.add_paragraph("");
1878        p.add_run("1.1 ").bold(true);
1879        p.add_run("The Company hereby employs the Employee as ");
1880        p.add_run("Chief Executive Officer (CEO)")
1881            .bold(true)
1882            .italic(true);
1883        p.add_run(", reporting directly to the Board of Directors.");
1884    }
1885    {
1886        let mut p = doc.add_paragraph("");
1887        p.add_run("1.2 ").bold(true);
1888        p.add_run("The Employee shall devote full working time, attention, and best efforts to \
1889                   the performance of duties as reasonably assigned by the Board, including but not limited to:");
1890    }
1891    doc.add_bullet_list_item(
1892        "Setting and executing the Company's strategic vision and business plan",
1893        0,
1894    );
1895    doc.add_bullet_list_item(
1896        "Overseeing all operations, engineering, and commercial activities",
1897        0,
1898    );
1899    doc.add_bullet_list_item(
1900        "Representing the Company to investors, customers, and the public",
1901        0,
1902    );
1903    doc.add_bullet_list_item("Recruiting, developing, and retaining key talent", 0);
1904
1905    {
1906        let mut p = doc.add_paragraph("");
1907        p.add_run("1.3 ").bold(true);
1908        p.add_run(
1909            "The Employee's primary work location shall be the Company's San Francisco \
1910                   headquarters, with reasonable travel as required by business needs.",
1911        );
1912    }
1913
1914    // ── Article 2: Compensation ──
1915    doc.add_paragraph("Article 2 — Compensation and Benefits")
1916        .style("Heading1");
1917    {
1918        let mut p = doc.add_paragraph("");
1919        p.add_run("2.1 Base Salary. ").bold(true);
1920        p.add_run("The Company shall pay the Employee an annual base salary of ");
1921        p.add_run("$375,000.00").bold(true);
1922        p.add_run(" (Three Hundred Seventy-Five Thousand Dollars), payable in accordance with the \
1923                   Company's standard payroll schedule, less applicable withholdings and deductions.");
1924    }
1925    {
1926        let mut p = doc.add_paragraph("");
1927        p.add_run("2.2 Annual Bonus. ").bold(true);
1928        p.add_run("The Employee shall be eligible for an annual performance bonus of up to ");
1929        p.add_run("40%").bold(true);
1930        p.add_run(
1931            " of base salary, based on achievement of mutually agreed performance objectives.",
1932        );
1933    }
1934    {
1935        let mut p = doc.add_paragraph("");
1936        p.add_run("2.3 Equity. ").bold(true);
1937        p.add_run("Subject to Board approval, the Employee shall receive a stock option grant of ");
1938        p.add_run("500,000 shares").bold(true);
1939        p.add_run(
1940            " of the Company's common stock, vesting over four (4) years with a one-year cliff, \
1941                   at an exercise price equal to the fair market value on the date of grant.",
1942        );
1943    }
1944
1945    // Compensation summary table
1946    doc.add_paragraph("");
1947    {
1948        let mut tbl = doc
1949            .add_table(5, 2)
1950            .borders(BorderStyle::Single, 4, "5B2C6F")
1951            .width_pct(70.0)
1952            .alignment(Alignment::Center);
1953        tbl.cell(0, 0).unwrap().set_text("Compensation Element");
1954        tbl.cell(0, 0).unwrap().shading("5B2C6F");
1955        tbl.cell(0, 1).unwrap().set_text("Value");
1956        tbl.cell(0, 1).unwrap().shading("5B2C6F");
1957        let items = [
1958            ("Base Salary", "$375,000/year"),
1959            ("Target Bonus", "Up to 40% ($150,000)"),
1960            ("Equity Grant", "500,000 shares (4yr vest)"),
1961            ("Total Target Comp", "$525,000 + equity"),
1962        ];
1963        for (i, (elem, val)) in items.iter().enumerate() {
1964            tbl.cell(i + 1, 0).unwrap().set_text(elem);
1965            tbl.cell(i + 1, 1).unwrap().set_text(val);
1966            if i == 3 {
1967                tbl.cell(i + 1, 0).unwrap().shading("E8DAEF");
1968                tbl.cell(i + 1, 1).unwrap().shading("E8DAEF");
1969            }
1970        }
1971    }
1972
1973    // ── Article 3: Benefits ──
1974    doc.add_paragraph("").page_break_before(true);
1975    doc.add_paragraph("Article 3 — Benefits").style("Heading1");
1976    {
1977        let mut p = doc.add_paragraph("");
1978        p.add_run("3.1 ").bold(true);
1979        p.add_run(
1980            "The Employee shall be entitled to participate in all benefit programs \
1981                   generally available to senior executives, including:",
1982        );
1983    }
1984    doc.add_bullet_list_item(
1985        "Medical, dental, and vision insurance (100% premium coverage for employee and dependents)",
1986        0,
1987    );
1988    doc.add_bullet_list_item("401(k) retirement plan with 6% company match", 0);
1989    doc.add_bullet_list_item("Life insurance and long-term disability coverage", 0);
1990    doc.add_bullet_list_item("Annual professional development allowance of $10,000", 0);
1991
1992    {
1993        let mut p = doc.add_paragraph("");
1994        p.add_run("3.2 Paid Time Off. ").bold(true);
1995        p.add_run(
1996            "The Employee shall receive 25 days of paid vacation per year, plus Company holidays, \
1997                   accruing on a monthly basis.",
1998        );
1999    }
2000
2001    // ── Article 4: Term and Termination ──
2002    doc.add_paragraph("Article 4 — Term and Termination")
2003        .style("Heading1");
2004    {
2005        let mut p = doc.add_paragraph("");
2006        p.add_run("4.1 At-Will Employment. ").bold(true);
2007        p.add_run(
2008            "This Agreement is for at-will employment and may be terminated by either Party \
2009                   at any time, with or without cause, subject to the notice provisions herein.",
2010        );
2011    }
2012    {
2013        let mut p = doc.add_paragraph("");
2014        p.add_run("4.2 Notice Period. ").bold(true);
2015        p.add_run("Either Party shall provide ");
2016        p.add_run("ninety (90) days").bold(true);
2017        p.add_run(" written notice of termination, or pay in lieu thereof.");
2018    }
2019    {
2020        let mut p = doc.add_paragraph("");
2021        p.add_run("4.3 Severance. ").bold(true);
2022        p.add_run("In the event of termination by the Company without Cause, the Employee shall \
2023                   receive (a) twelve (12) months of base salary continuation, (b) pro-rata bonus \
2024                   for the year of termination, and (c) twelve (12) months of COBRA premium coverage.");
2025    }
2026
2027    // ── Article 5: Confidentiality ──
2028    doc.add_paragraph("Article 5 — Confidentiality and IP")
2029        .style("Heading1");
2030    {
2031        let mut p = doc.add_paragraph("");
2032        p.add_run("5.1 ").bold(true);
2033        p.add_run(
2034            "The Employee agrees to maintain strict confidentiality of all proprietary \
2035                   information, trade secrets, and business plans of the Company during and after \
2036                   employment.",
2037        );
2038    }
2039    {
2040        let mut p = doc.add_paragraph("");
2041        p.add_run("5.2 ").bold(true);
2042        p.add_run(
2043            "All intellectual property, inventions, and works of authorship created by the \
2044                   Employee during the term of employment and within the scope of duties shall be \
2045                   the sole and exclusive property of the Company.",
2046        );
2047    }
2048
2049    // ── Article 6: Non-Compete ──
2050    doc.add_paragraph("Article 6 — Non-Competition")
2051        .style("Heading1");
2052    {
2053        let mut p = doc.add_paragraph("");
2054        p.add_run("6.1 ").bold(true);
2055        p.add_run("For a period of twelve (12) months following termination, the Employee agrees \
2056                   not to engage in any business that directly competes with the Company's core \
2057                   business of AI infrastructure and ML operations services, within the United States.");
2058    }
2059
2060    // ── Article 7: General Provisions ──
2061    doc.add_paragraph("Article 7 — General Provisions")
2062        .style("Heading1");
2063    {
2064        let mut p = doc.add_paragraph("");
2065        p.add_run("7.1 Governing Law. ").bold(true);
2066        p.add_run(
2067            "This Agreement shall be governed by and construed in accordance with the laws of \
2068                   the State of California.",
2069        );
2070    }
2071    {
2072        let mut p = doc.add_paragraph("");
2073        p.add_run("7.2 Entire Agreement. ").bold(true);
2074        p.add_run(
2075            "This Agreement constitutes the entire agreement between the Parties and supersedes \
2076                   all prior negotiations, representations, and agreements.",
2077        );
2078    }
2079    {
2080        let mut p = doc.add_paragraph("");
2081        p.add_run("7.3 Amendment. ").bold(true);
2082        p.add_run(
2083            "This Agreement may only be amended by a written instrument signed by both Parties.",
2084        );
2085    }
2086
2087    // ── Signature Block ──
2088    doc.add_paragraph("").page_break_before(true);
2089    doc.add_paragraph("IN WITNESS WHEREOF, the Parties have executed this Employment Agreement as of the Effective Date.")
2090        .space_after(Length::pt(24.0));
2091
2092    // Signature table
2093    {
2094        let mut tbl = doc.add_table(6, 2).width_pct(100.0);
2095        tbl.cell(0, 0).unwrap().set_text("FOR THE COMPANY:");
2096        tbl.cell(0, 0).unwrap().shading("5B2C6F");
2097        tbl.cell(0, 1).unwrap().set_text("EMPLOYEE:");
2098        tbl.cell(0, 1).unwrap().shading("5B2C6F");
2099
2100        tbl.cell(1, 0).unwrap().set_text("");
2101        tbl.cell(1, 1).unwrap().set_text("");
2102        tbl.row(1).unwrap().height(Length::pt(40.0));
2103
2104        tbl.cell(2, 0)
2105            .unwrap()
2106            .set_text("Signature: ___________________________");
2107        tbl.cell(2, 1)
2108            .unwrap()
2109            .set_text("Signature: ___________________________");
2110        tbl.cell(3, 0)
2111            .unwrap()
2112            .set_text("Name: Board Representative");
2113        tbl.cell(3, 1).unwrap().set_text("Name: Walter White");
2114        tbl.cell(4, 0)
2115            .unwrap()
2116            .set_text("Title: Chair, Board of Directors");
2117        tbl.cell(4, 1)
2118            .unwrap()
2119            .set_text("Title: Chief Executive Officer");
2120        tbl.cell(5, 0).unwrap().set_text("Date: _______________");
2121        tbl.cell(5, 1).unwrap().set_text("Date: _______________");
2122    }
2123
2124    doc
2125}
Source

pub fn add_paragraph(&mut self, text: &str) -> Paragraph<'_>

Add a paragraph to the cell and return a mutable reference.

Examples found in repository?
examples/generate_all_samples.rs (line 1227)
1204fn generate_invoice(_samples_dir: &Path) -> Document {
1205    let mut doc = Document::new();
1206
1207    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1208    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1209    doc.set_margins(
1210        Length::inches(0.75),
1211        Length::inches(0.75),
1212        Length::inches(0.75),
1213        Length::inches(0.75),
1214    );
1215    doc.set_title("Invoice INV-2026-0392");
1216    doc.set_author("Walter White");
1217    doc.set_keywords("invoice, Tensorbee");
1218
1219    doc.set_footer("Tensorbee — Thank you for your business!");
1220
1221    // ── Company & Invoice Header ──
1222    {
1223        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1224        // Company name left, INVOICE right
1225        {
1226            let mut cell = tbl.cell(0, 0).unwrap();
1227            let mut p = cell.add_paragraph("");
1228            p.add_run("TENSORBEE")
1229                .bold(true)
1230                .size(32.0)
1231                .color("B22222")
1232                .font("Helvetica");
1233        }
1234        {
1235            let mut cell = tbl.cell(0, 1).unwrap();
1236            let mut p = cell.add_paragraph("");
1237            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1238        }
1239        tbl.cell(1, 0)
1240            .unwrap()
1241            .set_text("123 Innovation Drive, Suite 400");
1242        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1243        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1244        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1245        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1246        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1247    }
1248
1249    doc.add_paragraph("")
1250        .border_bottom(BorderStyle::Thick, 8, "B22222");
1251    doc.add_paragraph("");
1252
1253    // ── Bill To / Ship To ──
1254    {
1255        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1256        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1257        tbl.cell(0, 0).unwrap().shading("B22222");
1258        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1259        tbl.cell(0, 1).unwrap().shading("B22222");
1260        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1261        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1262        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1263        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1264        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1265        tbl.cell(3, 1)
1266            .unwrap()
1267            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1268    }
1269
1270    doc.add_paragraph("");
1271
1272    // ── Line Items ──
1273    {
1274        let mut tbl = doc
1275            .add_table(8, 5)
1276            .borders(BorderStyle::Single, 2, "B22222")
1277            .width_pct(100.0);
1278        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1279        for (c, h) in hdrs.iter().enumerate() {
1280            tbl.cell(0, c).unwrap().set_text(h);
1281            tbl.cell(0, c).unwrap().shading("B22222");
1282        }
1283        let items = [
1284            (
1285                "ML Infrastructure Setup — Phase 1",
1286                "1",
1287                "$45,000.00",
1288                "$3,881.25",
1289                "$48,881.25",
1290            ),
1291            (
1292                "Data Pipeline Development (160 hrs)",
1293                "160",
1294                "$225.00",
1295                "$3,105.00",
1296                "$39,105.00",
1297            ),
1298            (
1299                "GPU Cluster Configuration",
1300                "1",
1301                "$12,000.00",
1302                "$1,035.00",
1303                "$13,035.00",
1304            ),
1305            (
1306                "API Gateway Implementation",
1307                "1",
1308                "$18,500.00",
1309                "$1,595.63",
1310                "$20,095.63",
1311            ),
1312            (
1313                "Load Testing & QA (80 hrs)",
1314                "80",
1315                "$195.00",
1316                "$1,345.50",
1317                "$16,945.50",
1318            ),
1319            (
1320                "Documentation & Training",
1321                "1",
1322                "$8,000.00",
1323                "$690.00",
1324                "$8,690.00",
1325            ),
1326            (
1327                "Project Management (3 months)",
1328                "3",
1329                "$6,500.00",
1330                "$1,679.63",
1331                "$21,179.63",
1332            ),
1333        ];
1334        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1335            let r = i + 1;
1336            tbl.cell(r, 0).unwrap().set_text(desc);
1337            tbl.cell(r, 1).unwrap().set_text(qty);
1338            tbl.cell(r, 2).unwrap().set_text(unit);
1339            tbl.cell(r, 3).unwrap().set_text(tax);
1340            tbl.cell(r, 4).unwrap().set_text(amt);
1341            if i % 2 == 0 {
1342                for c in 0..5 {
1343                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1344                }
1345            }
1346        }
1347    }
1348
1349    doc.add_paragraph("");
1350
1351    // ── Totals ──
1352    {
1353        let mut tbl = doc
1354            .add_table(4, 2)
1355            .borders(BorderStyle::Single, 2, "B22222")
1356            .width(Length::inches(3.0))
1357            .alignment(Alignment::Right);
1358        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1359        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1360        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1361        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1362        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1363        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1364        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1365        tbl.cell(3, 0).unwrap().shading("B22222");
1366        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1367        tbl.cell(3, 1).unwrap().shading("B22222");
1368    }
1369
1370    doc.add_paragraph("");
1371    doc.add_paragraph("");
1372
1373    // ── Payment Details ──
1374    {
1375        let mut p = doc.add_paragraph("");
1376        p.add_run("Payment Details")
1377            .bold(true)
1378            .size(14.0)
1379            .color("333333");
1380    }
1381    doc.add_paragraph("Bank: Silicon Valley Bank")
1382        .indent_left(Length::inches(0.3));
1383    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1384        .indent_left(Length::inches(0.3));
1385    doc.add_paragraph("Routing: 121140399")
1386        .indent_left(Length::inches(0.3));
1387    doc.add_paragraph("Swift: SVBKUS6S")
1388        .indent_left(Length::inches(0.3));
1389
1390    doc.add_paragraph("");
1391
1392    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1393        .shading("FAF0F0")
1394        .border_all(BorderStyle::Single, 2, "B22222");
1395
1396    doc
1397}
More examples
Hide additional examples
examples/styled_tables.rs (line 332)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
Source

pub fn paragraphs(&self) -> impl Iterator<Item = ParagraphRef<'_>>

Get an iterator over immutable paragraph references.

Source

pub fn width(self, length: Length) -> Self

Set cell width.

Examples found in repository?
examples/styled_tables.rs (line 396)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
More examples
Hide additional examples
examples/generate_all_samples.rs (line 474)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
Source

pub fn shading(self, fill_color: &str) -> Self

Set cell background shading color.

Examples found in repository?
examples/template_replace.rs (line 107)
43fn create_template(path: &Path) {
44    let mut doc = Document::new();
45    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
46    doc.set_margins(
47        Length::inches(1.0),
48        Length::inches(1.0),
49        Length::inches(1.0),
50        Length::inches(1.0),
51    );
52
53    doc.set_header("{{company_name}} — Confidential");
54    doc.set_footer("Prepared by {{author_name}} on {{date}}");
55
56    // ── Title ──
57    doc.add_paragraph("{{company_name}}")
58        .style("Heading1")
59        .alignment(Alignment::Center);
60
61    doc.add_paragraph("Project Proposal")
62        .alignment(Alignment::Center);
63
64    doc.add_paragraph("");
65
66    // ── Summary section ──
67    doc.add_paragraph("Executive Summary").style("Heading2");
68
69    doc.add_paragraph(
70        "This proposal outlines the {{project_name}} project for {{company_name}}. \
71         The primary contact is {{contact_name}} ({{contact_email}}). \
72         The proposed start date is {{start_date}} with an estimated duration of {{duration}}.",
73    );
74
75    doc.add_paragraph("");
76
77    // ── Cross-run placeholder (bold label + normal value) ──
78    doc.add_paragraph("Key Details").style("Heading2");
79
80    {
81        let mut p = doc.add_paragraph("");
82        p.add_run("Project: ").bold(true);
83        p.add_run("{{project_name}}");
84    }
85    {
86        let mut p = doc.add_paragraph("");
87        p.add_run("Budget: ").bold(true);
88        p.add_run("{{budget}}");
89    }
90    {
91        let mut p = doc.add_paragraph("");
92        p.add_run("Status: ").bold(true);
93        p.add_run("{{status}}");
94    }
95
96    doc.add_paragraph("");
97
98    // ── Table with placeholders ──
99    doc.add_paragraph("Team Members").style("Heading2");
100
101    {
102        let mut tbl = doc.add_table(4, 3);
103        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
104
105        // Header row
106        for col in 0..3 {
107            tbl.cell(0, col).unwrap().shading("2E75B6");
108        }
109        tbl.cell(0, 0).unwrap().set_text("Name");
110        tbl.cell(0, 1).unwrap().set_text("Role");
111        tbl.cell(0, 2).unwrap().set_text("Email");
112
113        tbl.cell(1, 0).unwrap().set_text("{{member1_name}}");
114        tbl.cell(1, 1).unwrap().set_text("{{member1_role}}");
115        tbl.cell(1, 2).unwrap().set_text("{{member1_email}}");
116
117        tbl.cell(2, 0).unwrap().set_text("{{member2_name}}");
118        tbl.cell(2, 1).unwrap().set_text("{{member2_role}}");
119        tbl.cell(2, 2).unwrap().set_text("{{member2_email}}");
120
121        tbl.cell(3, 0).unwrap().set_text("{{member3_name}}");
122        tbl.cell(3, 1).unwrap().set_text("{{member3_role}}");
123        tbl.cell(3, 2).unwrap().set_text("{{member3_email}}");
124    }
125
126    doc.add_paragraph("");
127
128    // ── Deliverables section ──
129    doc.add_paragraph("Deliverables").style("Heading2");
130
131    doc.add_paragraph("INSERTION_POINT");
132
133    doc.add_paragraph("");
134
135    // ── Signature block ──
136    doc.add_paragraph("Acceptance").style("Heading2");
137
138    doc.add_paragraph(
139        "By signing below, {{company_name}} agrees to the terms outlined in this proposal.",
140    );
141
142    {
143        let mut tbl = doc.add_table(2, 2);
144        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
145        tbl.cell(0, 0)
146            .unwrap()
147            .set_text("Customer: ___________________");
148        tbl.cell(0, 1)
149            .unwrap()
150            .set_text("Provider: ___________________");
151        tbl.cell(1, 0).unwrap().set_text("Date: {{date}}");
152        tbl.cell(1, 1).unwrap().set_text("Date: {{date}}");
153    }
154
155    doc.set_title("{{company_name}} — Project Proposal Template");
156    doc.set_author("Template Generator");
157
158    doc.save(path).unwrap();
159}
160
161/// Open the template, replace all placeholders, insert content, and save.
162fn fill_template(template_path: &Path, output_path: &Path) {
163    let mut doc = Document::open(template_path).unwrap();
164
165    // ── Batch replacement ──
166    let mut replacements = HashMap::new();
167    replacements.insert("{{company_name}}", "Riverside Medical Center");
168    replacements.insert("{{project_name}}", "Network Security Upgrade");
169    replacements.insert("{{contact_name}}", "Dr. Sarah Chen");
170    replacements.insert("{{contact_email}}", "s.chen@riverside.org");
171    replacements.insert("{{start_date}}", "March 1, 2026");
172    replacements.insert("{{duration}}", "12 weeks");
173    replacements.insert("{{budget}}", "$185,000");
174    replacements.insert("{{status}}", "Pending Approval");
175    replacements.insert("{{author_name}}", "James Wilson");
176    replacements.insert("{{date}}", "February 22, 2026");
177
178    // Team members
179    replacements.insert("{{member1_name}}", "James Wilson");
180    replacements.insert("{{member1_role}}", "Project Lead");
181    replacements.insert("{{member1_email}}", "j.wilson@provider.com");
182    replacements.insert("{{member2_name}}", "Maria Garcia");
183    replacements.insert("{{member2_role}}", "Security Architect");
184    replacements.insert("{{member2_email}}", "m.garcia@provider.com");
185    replacements.insert("{{member3_name}}", "David Park");
186    replacements.insert("{{member3_role}}", "Network Engineer");
187    replacements.insert("{{member3_email}}", "d.park@provider.com");
188
189    let count = doc.replace_all(&replacements);
190    println!("  Replaced {} placeholders", count);
191
192    // ── Insert deliverables at the insertion point ──
193    if let Some(idx) = doc.find_content_index("INSERTION_POINT") {
194        // Remove the placeholder paragraph
195        doc.remove_content(idx);
196
197        // Insert deliverables list
198        doc.insert_paragraph(idx, "The following deliverables are included:");
199
200        // Insert a deliverables table
201        let mut tbl = doc.insert_table(idx + 1, 5, 3);
202        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
203
204        for col in 0..3 {
205            tbl.cell(0, col).unwrap().shading("E2EFDA");
206        }
207        tbl.cell(0, 0).unwrap().set_text("Phase");
208        tbl.cell(0, 1).unwrap().set_text("Description");
209        tbl.cell(0, 2).unwrap().set_text("Timeline");
210
211        tbl.cell(1, 0).unwrap().set_text("1. Discovery");
212        tbl.cell(1, 1)
213            .unwrap()
214            .set_text("Network assessment and asset inventory");
215        tbl.cell(1, 2).unwrap().set_text("Weeks 1-3");
216
217        tbl.cell(2, 0).unwrap().set_text("2. Design");
218        tbl.cell(2, 1)
219            .unwrap()
220            .set_text("Security architecture and policy design");
221        tbl.cell(2, 2).unwrap().set_text("Weeks 4-6");
222
223        tbl.cell(3, 0).unwrap().set_text("3. Implementation");
224        tbl.cell(3, 1)
225            .unwrap()
226            .set_text("Deploy monitoring and access controls");
227        tbl.cell(3, 2).unwrap().set_text("Weeks 7-10");
228
229        tbl.cell(4, 0).unwrap().set_text("4. Validation");
230        tbl.cell(4, 1)
231            .unwrap()
232            .set_text("Testing, training, and handover");
233        tbl.cell(4, 2).unwrap().set_text("Weeks 11-12");
234
235        println!("  Inserted deliverables table at position {}", idx);
236    }
237
238    // ── Update metadata ──
239    doc.set_title("Riverside Medical Center — Network Security Upgrade Proposal");
240    doc.set_author("James Wilson");
241    doc.set_subject("Project Proposal");
242    doc.set_keywords("security, network, medical, proposal");
243
244    doc.save(output_path).unwrap();
245}
More examples
Hide additional examples
examples/styled_tables.rs (line 53)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
examples/generate_samples.rs (line 304)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 357)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
684
685// =============================================================================
686// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
687// =============================================================================
688fn generate_proposal(_samples_dir: &Path) -> Document {
689    let mut doc = Document::new();
690
691    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
692    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
693    doc.set_margins(
694        Length::inches(1.0),
695        Length::inches(1.0),
696        Length::inches(1.0),
697        Length::inches(1.0),
698    );
699    doc.set_title("AI Platform Modernization Proposal");
700    doc.set_author("Walter White");
701    doc.set_subject("Technology Proposal");
702    doc.set_keywords("proposal, AI, modernization, Tensorbee");
703
704    // Banner header
705    let logo_img = create_logo_png(220, 48);
706    let banner = build_header_banner_xml(
707        "rId1",
708        &BannerOpts {
709            bg_color: "1B2A4A",
710            banner_width: 7772400,
711            banner_height: 914400,
712            logo_width: 2011680,
713            logo_height: 438912,
714            logo_x_offset: 295125,
715            logo_y_offset: 237744,
716        },
717    );
718    doc.set_raw_header_with_images(
719        banner,
720        &[("rId1", &logo_img, "logo.png")],
721        rdocx_oxml::header_footer::HdrFtrType::Default,
722    );
723    doc.set_different_first_page(true);
724    let cover_banner = build_header_banner_xml(
725        "rId1",
726        &BannerOpts {
727            bg_color: "C5922E",
728            banner_width: 7772400,
729            banner_height: 914400,
730            logo_width: 2011680,
731            logo_height: 438912,
732            logo_x_offset: 295125,
733            logo_y_offset: 237744,
734        },
735    );
736    doc.set_raw_header_with_images(
737        cover_banner,
738        &[("rId1", &logo_img, "logo.png")],
739        rdocx_oxml::header_footer::HdrFtrType::First,
740    );
741    doc.set_margins(
742        Length::twips(2292),
743        Length::twips(1440),
744        Length::twips(1440),
745        Length::twips(1440),
746    );
747    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
748    doc.set_footer("Tensorbee — Confidential");
749
750    // Custom styles
751    doc.add_style(
752        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
753            .based_on("Normal")
754            .run_properties({
755                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
756                rpr.bold = Some(true);
757                rpr.font_ascii = Some("Georgia".to_string());
758                rpr.font_hansi = Some("Georgia".to_string());
759                rpr.sz = Some(rdocx_oxml::HalfPoint(56)); // half-points → 28pt
760                rpr.color = Some("1B2A4A".to_string());
761                rpr
762            }),
763    );
764
765    // ── Cover Page ──
766    for _ in 0..4 {
767        doc.add_paragraph("");
768    }
769    doc.add_paragraph("AI Platform Modernization")
770        .style("ProposalTitle")
771        .alignment(Alignment::Center);
772    doc.add_paragraph("")
773        .alignment(Alignment::Center)
774        .border_bottom(BorderStyle::Single, 8, "C5922E");
775    {
776        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
777        p.add_run("Prepared for Global Financial Corp.")
778            .size(14.0)
779            .color("666666")
780            .italic(true);
781    }
782    {
783        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
784        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
785            .size(12.0)
786            .color("888888");
787    }
788    {
789        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
790        p.add_run("February 22, 2026").size(12.0).color("888888");
791    }
792
793    // ── TOC ──
794    doc.add_paragraph("").page_break_before(true);
795    doc.add_paragraph("Table of Contents").style("Heading1");
796    // We'll add a manual-style TOC since the insert_toc goes at a specific position
797    doc.insert_toc(doc.content_count(), 2);
798
799    // ── Executive Summary ──
800    doc.add_paragraph("").page_break_before(true);
801    doc.add_paragraph("Executive Summary").style("Heading1");
802    doc.add_paragraph(
803        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
804         transitioning from legacy batch-processing systems to a real-time inference architecture. \
805         This transformation will reduce model deployment time from weeks to hours, improve \
806         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
807    )
808    .first_line_indent(Length::inches(0.3));
809    doc.add_paragraph(
810        "The project spans three phases over 18 months, with a total investment of $2.8M. \
811         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
812         similar transformations for three Fortune 500 financial institutions.",
813    )
814    .first_line_indent(Length::inches(0.3));
815
816    // Key metrics table
817    doc.add_paragraph("");
818    {
819        let mut tbl = doc
820            .add_table(5, 2)
821            .borders(BorderStyle::Single, 4, "1B2A4A")
822            .width_pct(80.0);
823        tbl.cell(0, 0).unwrap().set_text("Key Metric");
824        tbl.cell(0, 0).unwrap().shading("1B2A4A");
825        tbl.cell(0, 1).unwrap().set_text("Value");
826        tbl.cell(0, 1).unwrap().shading("1B2A4A");
827        let rows = [
828            ("Total Investment", "$2.8M"),
829            ("Annual Savings", "$4.2M"),
830            ("ROI (Year 1)", "150%"),
831            ("Timeline", "18 months"),
832        ];
833        for (i, (k, v)) in rows.iter().enumerate() {
834            tbl.cell(i + 1, 0).unwrap().set_text(k);
835            if i % 2 == 0 {
836                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
837            }
838            tbl.cell(i + 1, 1).unwrap().set_text(v);
839            if i % 2 == 0 {
840                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
841            }
842        }
843    }
844
845    // ── Problem Statement ──
846    doc.add_paragraph("").page_break_before(true);
847    doc.add_paragraph("Problem Statement").style("Heading1");
848    doc.add_paragraph(
849        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
850    );
851    doc.add_numbered_list_item("Deployment Latency — New models take 3-4 weeks to deploy to production, compared to the industry benchmark of 2-3 days.", 0);
852    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
853    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
854
855    // ── Proposed Solution ──
856    doc.add_paragraph("").page_break_before(true);
857    doc.add_paragraph("Proposed Solution").style("Heading1");
858
859    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
860        .style("Heading2");
861    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
862    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
863    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
864
865    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
866        .style("Heading2");
867    doc.add_bullet_list_item(
868        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
869        0,
870    );
871    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
872    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
873
874    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
875        .style("Heading2");
876    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
877    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
878    doc.add_bullet_list_item(
879        "Self-service model deployment portal for data science teams",
880        0,
881    );
882
883    // ── Budget ──
884    doc.add_paragraph("Budget Breakdown").style("Heading1");
885    {
886        let mut tbl = doc
887            .add_table(6, 3)
888            .borders(BorderStyle::Single, 4, "1B2A4A")
889            .width_pct(100.0);
890        tbl.cell(0, 0).unwrap().set_text("Category");
891        tbl.cell(0, 0).unwrap().shading("1B2A4A");
892        tbl.cell(0, 1).unwrap().set_text("Cost");
893        tbl.cell(0, 1).unwrap().shading("1B2A4A");
894        tbl.cell(0, 2).unwrap().set_text("Notes");
895        tbl.cell(0, 2).unwrap().shading("1B2A4A");
896        let items = [
897            (
898                "Infrastructure",
899                "$450,000",
900                "Cloud compute + GPU instances",
901            ),
902            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
903            (
904                "Software Licenses",
905                "$350,000",
906                "Kafka, monitoring, CI/CD tools",
907            ),
908            (
909                "Training & Enablement",
910                "$200,000",
911                "Team training, documentation",
912            ),
913            ("Contingency (10%)", "$200,000", "Risk buffer"),
914        ];
915        for (i, (cat, cost, note)) in items.iter().enumerate() {
916            tbl.cell(i + 1, 0).unwrap().set_text(cat);
917            tbl.cell(i + 1, 1).unwrap().set_text(cost);
918            tbl.cell(i + 1, 2).unwrap().set_text(note);
919            if i % 2 == 0 {
920                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
921                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
922                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
923            }
924        }
925    }
926
927    // ── Closing ──
928    doc.add_paragraph("");
929    doc.add_paragraph("Next Steps").style("Heading1");
930    doc.add_numbered_list_item(
931        "Schedule technical deep-dive with GFC engineering team (Week 1)",
932        0,
933    );
934    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
935    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
936
937    doc.add_paragraph("");
938    {
939        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
940        p.add_run("Walter White")
941            .bold(true)
942            .size(14.0)
943            .color("1B2A4A");
944    }
945    {
946        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
947        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
948            .size(10.0)
949            .color("888888");
950    }
951
952    doc
953}
954
955// =============================================================================
956// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
957// =============================================================================
958fn generate_quote(_samples_dir: &Path) -> Document {
959    let mut doc = Document::new();
960
961    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
962    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
963    doc.set_margins(
964        Length::inches(0.75),
965        Length::inches(0.75),
966        Length::inches(0.75),
967        Length::inches(0.75),
968    );
969    doc.set_title("Quotation QT-2026-0147");
970    doc.set_author("Walter White");
971    doc.set_keywords("quote, BOM, Tensorbee");
972
973    doc.set_header("Tensorbee — Quotation");
974    doc.set_footer("QT-2026-0147 | Page");
975
976    // ── Header Block ──
977    {
978        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
979        p.add_run("TENSORBEE")
980            .bold(true)
981            .size(28.0)
982            .color("008B8B")
983            .font("Helvetica");
984    }
985    doc.add_paragraph("123 Innovation Drive, Suite 400")
986        .alignment(Alignment::Left);
987    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
988        .alignment(Alignment::Left);
989    doc.add_paragraph("")
990        .border_bottom(BorderStyle::Single, 8, "008B8B");
991
992    // Quote meta
993    doc.add_paragraph("");
994    {
995        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
996        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
997        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
998        tbl.cell(0, 2).unwrap().set_text("DATE");
999        tbl.cell(0, 2).unwrap().shading("008B8B");
1000        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1001        tbl.cell(0, 3).unwrap().shading("008B8B");
1002        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1003        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1004        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1005        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1006        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1007        tbl.cell(2, 1).unwrap().set_text("Walter White");
1008        tbl.cell(2, 2).unwrap().set_text("Payment:");
1009        tbl.cell(2, 3).unwrap().set_text("Net 30");
1010        tbl.cell(3, 0).unwrap().set_text("Customer:");
1011        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1012        tbl.cell(3, 2).unwrap().set_text("Currency:");
1013        tbl.cell(3, 3).unwrap().set_text("USD");
1014    }
1015
1016    doc.add_paragraph("");
1017
1018    // ── Bill of Materials ──
1019    {
1020        let mut p = doc.add_paragraph("");
1021        p.add_run("Bill of Materials")
1022            .bold(true)
1023            .size(16.0)
1024            .color("1A3C3C");
1025    }
1026    doc.add_paragraph("");
1027
1028    {
1029        let mut tbl = doc
1030            .add_table(10, 6)
1031            .borders(BorderStyle::Single, 2, "008B8B")
1032            .width_pct(100.0)
1033            .layout_fixed();
1034        // Headers
1035        let hdrs = [
1036            "#",
1037            "Part Number",
1038            "Description",
1039            "Qty",
1040            "Unit Price",
1041            "Total",
1042        ];
1043        for (c, h) in hdrs.iter().enumerate() {
1044            tbl.cell(0, c).unwrap().set_text(h);
1045            tbl.cell(0, c).unwrap().shading("008B8B");
1046        }
1047
1048        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1049            (
1050                "TB-GPU-A100",
1051                "NVIDIA A100 80GB GPU",
1052                "4",
1053                "$12,500.00",
1054                "$50,000.00",
1055            ),
1056            (
1057                "TB-SRV-R750",
1058                "Dell R750xa Server Chassis",
1059                "2",
1060                "$8,200.00",
1061                "$16,400.00",
1062            ),
1063            (
1064                "TB-RAM-256G",
1065                "256GB DDR5 ECC Memory Module",
1066                "8",
1067                "$890.00",
1068                "$7,120.00",
1069            ),
1070            (
1071                "TB-SSD-3840",
1072                "3.84TB NVMe U.2 SSD",
1073                "8",
1074                "$1,150.00",
1075                "$9,200.00",
1076            ),
1077            (
1078                "TB-NET-CX7",
1079                "ConnectX-7 200GbE NIC",
1080                "4",
1081                "$1,800.00",
1082                "$7,200.00",
1083            ),
1084            (
1085                "TB-SW-48P",
1086                "48-Port 100GbE Switch",
1087                "1",
1088                "$22,000.00",
1089                "$22,000.00",
1090            ),
1091            (
1092                "TB-CAB-RACK",
1093                "42U Server Rack + PDU",
1094                "1",
1095                "$4,500.00",
1096                "$4,500.00",
1097            ),
1098            (
1099                "TB-SVC-INST",
1100                "Installation & Configuration",
1101                "1",
1102                "$8,500.00",
1103                "$8,500.00",
1104            ),
1105            (
1106                "TB-SVC-SUPP",
1107                "3-Year Premium Support",
1108                "1",
1109                "$15,000.00",
1110                "$15,000.00",
1111            ),
1112        ];
1113
1114        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1115            let row = i + 1;
1116            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1117            tbl.cell(row, 1).unwrap().set_text(pn);
1118            tbl.cell(row, 2).unwrap().set_text(desc);
1119            tbl.cell(row, 3).unwrap().set_text(qty);
1120            tbl.cell(row, 4).unwrap().set_text(unit);
1121            tbl.cell(row, 5).unwrap().set_text(total);
1122            if i % 2 == 0 {
1123                for c in 0..6 {
1124                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1125                }
1126            }
1127        }
1128    }
1129
1130    doc.add_paragraph("");
1131
1132    // ── Totals ──
1133    {
1134        let mut tbl = doc
1135            .add_table(4, 2)
1136            .borders(BorderStyle::Single, 2, "008B8B")
1137            .width(Length::inches(3.5))
1138            .alignment(Alignment::Right);
1139        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1140        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1141        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1142        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1143        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1144        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1145        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1146        tbl.cell(3, 0).unwrap().shading("E07020");
1147        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1148        tbl.cell(3, 1).unwrap().shading("E07020");
1149    }
1150
1151    doc.add_paragraph("");
1152
1153    // ── Terms & Conditions ──
1154    {
1155        let mut p = doc.add_paragraph("");
1156        p.add_run("Terms & Conditions")
1157            .bold(true)
1158            .size(14.0)
1159            .color("1A3C3C");
1160    }
1161    doc.add_numbered_list_item(
1162        "This quotation is valid for 30 calendar days from the date of issue.",
1163        0,
1164    );
1165    doc.add_numbered_list_item(
1166        "All prices are in USD and exclusive of applicable taxes unless stated.",
1167        0,
1168    );
1169    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1170    doc.add_numbered_list_item(
1171        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1172        0,
1173    );
1174    doc.add_numbered_list_item(
1175        "Warranty: 3-year manufacturer warranty on all hardware components.",
1176        0,
1177    );
1178    doc.add_numbered_list_item(
1179        "Returns subject to 15% restocking fee if initiated after 14 days.",
1180        0,
1181    );
1182
1183    doc.add_paragraph("");
1184    doc.add_paragraph("")
1185        .border_bottom(BorderStyle::Single, 4, "008B8B");
1186    doc.add_paragraph("");
1187    {
1188        let mut p = doc.add_paragraph("");
1189        p.add_run("Accepted by: ").bold(true);
1190        p.add_run("___________________________ Date: ___________");
1191    }
1192    {
1193        let mut p = doc.add_paragraph("");
1194        p.add_run("Print Name: ").bold(true);
1195        p.add_run("___________________________ Title: ___________");
1196    }
1197
1198    doc
1199}
1200
1201// =============================================================================
1202// 4. INVOICE — Crimson + charcoal scheme
1203// =============================================================================
1204fn generate_invoice(_samples_dir: &Path) -> Document {
1205    let mut doc = Document::new();
1206
1207    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1208    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1209    doc.set_margins(
1210        Length::inches(0.75),
1211        Length::inches(0.75),
1212        Length::inches(0.75),
1213        Length::inches(0.75),
1214    );
1215    doc.set_title("Invoice INV-2026-0392");
1216    doc.set_author("Walter White");
1217    doc.set_keywords("invoice, Tensorbee");
1218
1219    doc.set_footer("Tensorbee — Thank you for your business!");
1220
1221    // ── Company & Invoice Header ──
1222    {
1223        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1224        // Company name left, INVOICE right
1225        {
1226            let mut cell = tbl.cell(0, 0).unwrap();
1227            let mut p = cell.add_paragraph("");
1228            p.add_run("TENSORBEE")
1229                .bold(true)
1230                .size(32.0)
1231                .color("B22222")
1232                .font("Helvetica");
1233        }
1234        {
1235            let mut cell = tbl.cell(0, 1).unwrap();
1236            let mut p = cell.add_paragraph("");
1237            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1238        }
1239        tbl.cell(1, 0)
1240            .unwrap()
1241            .set_text("123 Innovation Drive, Suite 400");
1242        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1243        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1244        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1245        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1246        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1247    }
1248
1249    doc.add_paragraph("")
1250        .border_bottom(BorderStyle::Thick, 8, "B22222");
1251    doc.add_paragraph("");
1252
1253    // ── Bill To / Ship To ──
1254    {
1255        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1256        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1257        tbl.cell(0, 0).unwrap().shading("B22222");
1258        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1259        tbl.cell(0, 1).unwrap().shading("B22222");
1260        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1261        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1262        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1263        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1264        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1265        tbl.cell(3, 1)
1266            .unwrap()
1267            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1268    }
1269
1270    doc.add_paragraph("");
1271
1272    // ── Line Items ──
1273    {
1274        let mut tbl = doc
1275            .add_table(8, 5)
1276            .borders(BorderStyle::Single, 2, "B22222")
1277            .width_pct(100.0);
1278        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1279        for (c, h) in hdrs.iter().enumerate() {
1280            tbl.cell(0, c).unwrap().set_text(h);
1281            tbl.cell(0, c).unwrap().shading("B22222");
1282        }
1283        let items = [
1284            (
1285                "ML Infrastructure Setup — Phase 1",
1286                "1",
1287                "$45,000.00",
1288                "$3,881.25",
1289                "$48,881.25",
1290            ),
1291            (
1292                "Data Pipeline Development (160 hrs)",
1293                "160",
1294                "$225.00",
1295                "$3,105.00",
1296                "$39,105.00",
1297            ),
1298            (
1299                "GPU Cluster Configuration",
1300                "1",
1301                "$12,000.00",
1302                "$1,035.00",
1303                "$13,035.00",
1304            ),
1305            (
1306                "API Gateway Implementation",
1307                "1",
1308                "$18,500.00",
1309                "$1,595.63",
1310                "$20,095.63",
1311            ),
1312            (
1313                "Load Testing & QA (80 hrs)",
1314                "80",
1315                "$195.00",
1316                "$1,345.50",
1317                "$16,945.50",
1318            ),
1319            (
1320                "Documentation & Training",
1321                "1",
1322                "$8,000.00",
1323                "$690.00",
1324                "$8,690.00",
1325            ),
1326            (
1327                "Project Management (3 months)",
1328                "3",
1329                "$6,500.00",
1330                "$1,679.63",
1331                "$21,179.63",
1332            ),
1333        ];
1334        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1335            let r = i + 1;
1336            tbl.cell(r, 0).unwrap().set_text(desc);
1337            tbl.cell(r, 1).unwrap().set_text(qty);
1338            tbl.cell(r, 2).unwrap().set_text(unit);
1339            tbl.cell(r, 3).unwrap().set_text(tax);
1340            tbl.cell(r, 4).unwrap().set_text(amt);
1341            if i % 2 == 0 {
1342                for c in 0..5 {
1343                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1344                }
1345            }
1346        }
1347    }
1348
1349    doc.add_paragraph("");
1350
1351    // ── Totals ──
1352    {
1353        let mut tbl = doc
1354            .add_table(4, 2)
1355            .borders(BorderStyle::Single, 2, "B22222")
1356            .width(Length::inches(3.0))
1357            .alignment(Alignment::Right);
1358        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1359        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1360        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1361        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1362        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1363        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1364        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1365        tbl.cell(3, 0).unwrap().shading("B22222");
1366        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1367        tbl.cell(3, 1).unwrap().shading("B22222");
1368    }
1369
1370    doc.add_paragraph("");
1371    doc.add_paragraph("");
1372
1373    // ── Payment Details ──
1374    {
1375        let mut p = doc.add_paragraph("");
1376        p.add_run("Payment Details")
1377            .bold(true)
1378            .size(14.0)
1379            .color("333333");
1380    }
1381    doc.add_paragraph("Bank: Silicon Valley Bank")
1382        .indent_left(Length::inches(0.3));
1383    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1384        .indent_left(Length::inches(0.3));
1385    doc.add_paragraph("Routing: 121140399")
1386        .indent_left(Length::inches(0.3));
1387    doc.add_paragraph("Swift: SVBKUS6S")
1388        .indent_left(Length::inches(0.3));
1389
1390    doc.add_paragraph("");
1391
1392    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1393        .shading("FAF0F0")
1394        .border_all(BorderStyle::Single, 2, "B22222");
1395
1396    doc
1397}
1398
1399// =============================================================================
1400// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1401// =============================================================================
1402fn generate_report(_samples_dir: &Path) -> Document {
1403    let mut doc = Document::new();
1404
1405    // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1406    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1407    doc.set_margins(
1408        Length::inches(1.0),
1409        Length::inches(1.0),
1410        Length::inches(1.0),
1411        Length::inches(1.0),
1412    );
1413    doc.set_title("Q4 2025 Environmental Impact Report");
1414    doc.set_author("Walter White");
1415    doc.set_subject("Quarterly Environmental Report");
1416    doc.set_keywords("environment, sustainability, report, Tensorbee");
1417
1418    doc.set_different_first_page(true);
1419    doc.set_first_page_header("");
1420    doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1421    doc.set_footer("CONFIDENTIAL — Page");
1422
1423    // ── Cover ──
1424    let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1425    doc.add_background_image(&cover_bg, "report_cover.png");
1426
1427    for _ in 0..5 {
1428        doc.add_paragraph("");
1429    }
1430    {
1431        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1432        p.add_run("Q4 2025")
1433            .bold(true)
1434            .size(48.0)
1435            .color("FFFFFF")
1436            .font("Georgia");
1437    }
1438    {
1439        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440        p.add_run("Environmental Impact Report")
1441            .size(24.0)
1442            .color("90EE90")
1443            .font("Georgia")
1444            .italic(true);
1445    }
1446    doc.add_paragraph("");
1447    {
1448        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1449        p.add_run("Tensorbee — Sustainability Division")
1450            .size(14.0)
1451            .color("C0C0C0");
1452    }
1453    {
1454        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1455        p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1456            .size(11.0)
1457            .color("AAAAAA");
1458    }
1459
1460    // ── TOC ──
1461    doc.add_paragraph("").page_break_before(true);
1462    doc.insert_toc(doc.content_count(), 3);
1463
1464    // ── Section 1: Executive Overview ──
1465    doc.add_paragraph("").page_break_before(true);
1466    doc.add_paragraph("Executive Overview").style("Heading1");
1467    doc.add_paragraph(
1468        "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1469         sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1470         to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1471         100% renewable energy in our primary data centers and the launch of our carbon offset \
1472         marketplace.",
1473    )
1474    .first_line_indent(Length::inches(0.3));
1475
1476    // Image: performance chart
1477    let chart_img = create_chart_png(400, 200);
1478    doc.add_paragraph("");
1479    doc.add_picture(
1480        &chart_img,
1481        "performance_chart.png",
1482        Length::inches(5.0),
1483        Length::inches(2.5),
1484    )
1485    .alignment(Alignment::Center);
1486    {
1487        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1488        p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1489            .italic(true)
1490            .size(9.0)
1491            .color("666666");
1492    }
1493
1494    // ── Section 2: Energy Consumption ──
1495    doc.add_paragraph("").page_break_before(true);
1496    doc.add_paragraph("Energy Consumption").style("Heading1");
1497
1498    doc.add_paragraph("Data Center Operations")
1499        .style("Heading2");
1500    doc.add_paragraph(
1501        "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1502         a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1503         server consolidation.",
1504    );
1505
1506    // Data table
1507    {
1508        let mut tbl = doc
1509            .add_table(5, 4)
1510            .borders(BorderStyle::Single, 4, "2D5016")
1511            .width_pct(100.0);
1512        let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1513        for (c, h) in hdrs.iter().enumerate() {
1514            tbl.cell(0, c).unwrap().set_text(h);
1515            tbl.cell(0, c).unwrap().shading("2D5016");
1516        }
1517        let rows = [
1518            ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1519            ("Dublin (EU)", "2.1", "1.2", "1.18"),
1520            ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1521            ("TOTAL", "7.1", "4.2", "1.17 avg"),
1522        ];
1523        for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1524            tbl.cell(i + 1, 0).unwrap().set_text(dc);
1525            tbl.cell(i + 1, 1).unwrap().set_text(cap);
1526            tbl.cell(i + 1, 2).unwrap().set_text(use_);
1527            tbl.cell(i + 1, 3).unwrap().set_text(pue);
1528            if i == 3 {
1529                for c in 0..4 {
1530                    tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1531                }
1532            }
1533        }
1534    }
1535
1536    doc.add_paragraph("");
1537
1538    doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1539    doc.add_paragraph("Breakdown of energy sources across all facilities:");
1540
1541    doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1542    doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1543    doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1544    doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1545
1546    // ── Section 3: Water & Waste ──
1547    doc.add_paragraph("Water & Waste Management")
1548        .style("Heading1");
1549
1550    doc.add_paragraph("Water Usage").style("Heading2");
1551    doc.add_paragraph(
1552        "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1553         Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1554    );
1555
1556    doc.add_paragraph("Waste Reduction").style("Heading2");
1557    doc.add_paragraph("E-waste management results for Q4:")
1558        .keep_with_next(true);
1559    {
1560        let mut tbl = doc
1561            .add_table(4, 3)
1562            .borders(BorderStyle::Single, 2, "6B8E23")
1563            .width_pct(80.0);
1564        tbl.cell(0, 0).unwrap().set_text("Category");
1565        tbl.cell(0, 0).unwrap().shading("6B8E23");
1566        tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1567        tbl.cell(0, 1).unwrap().shading("6B8E23");
1568        tbl.cell(0, 2).unwrap().set_text("Recycled %");
1569        tbl.cell(0, 2).unwrap().shading("6B8E23");
1570        let rows = [
1571            ("Server Hardware", "2,450", "94%"),
1572            ("Networking Equipment", "820", "91%"),
1573            ("Storage Media", "340", "99%"),
1574        ];
1575        for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1576            tbl.cell(i + 1, 0).unwrap().set_text(cat);
1577            tbl.cell(i + 1, 1).unwrap().set_text(wt);
1578            tbl.cell(i + 1, 2).unwrap().set_text(pct);
1579        }
1580    }
1581
1582    // ── Section 4: Initiatives ──
1583    doc.add_paragraph("").page_break_before(true);
1584    doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1585
1586    doc.add_paragraph("Planned Programs").style("Heading2");
1587    doc.add_numbered_list_item(
1588        "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1589        0,
1590    );
1591    doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1592    doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1593    doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1594
1595    doc.add_paragraph("Investment Targets").style("Heading2");
1596    doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1597        .keep_with_next(true);
1598    {
1599        let mut tbl = doc
1600            .add_table(5, 2)
1601            .borders(BorderStyle::Single, 4, "2D5016");
1602        tbl.cell(0, 0).unwrap().set_text("Initiative");
1603        tbl.cell(0, 0).unwrap().shading("2D5016");
1604        tbl.cell(0, 1).unwrap().set_text("Budget");
1605        tbl.cell(0, 1).unwrap().shading("2D5016");
1606        let rows = [
1607            ("Battery Storage", "$1.2M"),
1608            ("Immersion Cooling Pilot", "$800K"),
1609            ("Solar Panel Expansion", "$2.1M"),
1610            ("Carbon Credits", "$500K"),
1611        ];
1612        for (i, (init, budget)) in rows.iter().enumerate() {
1613            tbl.cell(i + 1, 0).unwrap().set_text(init);
1614            tbl.cell(i + 1, 1).unwrap().set_text(budget);
1615            if i % 2 == 0 {
1616                tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1617                tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1618            }
1619        }
1620    }
1621
1622    // Image: sustainability roadmap
1623    let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1624    doc.add_paragraph("");
1625    doc.add_picture(
1626        &roadmap_img,
1627        "roadmap.png",
1628        Length::inches(6.0),
1629        Length::inches(1.2),
1630    )
1631    .alignment(Alignment::Center);
1632    {
1633        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1634        p.add_run("Figure 2: 2026 Sustainability Roadmap")
1635            .italic(true)
1636            .size(9.0)
1637            .color("666666");
1638    }
1639
1640    doc.add_paragraph("");
1641    doc.add_paragraph(
1642        "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1643    )
1644    .shading("FFFAF0")
1645    .border_all(BorderStyle::Single, 2, "2D5016");
1646
1647    doc
1648}
1649
1650// =============================================================================
1651// 6. LETTER — Slate blue + silver scheme
1652// =============================================================================
1653fn generate_letter(_samples_dir: &Path) -> Document {
1654    let mut doc = Document::new();
1655
1656    // Colors: Slate #4A5568, Blue accent #3182CE
1657    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1658    doc.set_margins(
1659        Length::inches(1.25),
1660        Length::inches(1.25),
1661        Length::inches(1.25),
1662        Length::inches(1.25),
1663    );
1664    doc.set_title("Business Letter");
1665    doc.set_author("Walter White");
1666
1667    // Banner header using raw XML
1668    let logo_img = create_logo_png(220, 48);
1669    let banner = build_header_banner_xml(
1670        "rId1",
1671        &BannerOpts {
1672            bg_color: "4A5568",
1673            banner_width: 7772400,
1674            banner_height: 731520, // ~0.8"
1675            logo_width: 2011680,
1676            logo_height: 438912,
1677            logo_x_offset: 295125,
1678            logo_y_offset: 146304,
1679        },
1680    );
1681    doc.set_raw_header_with_images(
1682        banner,
1683        &[("rId1", &logo_img, "logo.png")],
1684        rdocx_oxml::header_footer::HdrFtrType::Default,
1685    );
1686    doc.set_margins(
1687        Length::twips(2000),
1688        Length::twips(1800),
1689        Length::twips(1440),
1690        Length::twips(1800),
1691    );
1692    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
1693
1694    // Footer with contact
1695    doc.set_footer(
1696        "Tensorbee | 123 Innovation Drive, Suite 400 | San Francisco, CA 94105 | tensorbee.com",
1697    );
1698
1699    // ── Sender Address ──
1700    doc.add_paragraph("Walter White");
1701    doc.add_paragraph("Chief Executive Officer");
1702    doc.add_paragraph("Tensorbee");
1703    doc.add_paragraph("123 Innovation Drive, Suite 400");
1704    doc.add_paragraph("San Francisco, CA 94105");
1705    doc.add_paragraph("");
1706    doc.add_paragraph("February 22, 2026");
1707
1708    doc.add_paragraph("");
1709
1710    // ── Recipient ──
1711    doc.add_paragraph("Ms. Sarah Chen");
1712    doc.add_paragraph("VP of Engineering");
1713    doc.add_paragraph("Meridian Dynamics LLC");
1714    doc.add_paragraph("456 Enterprise Boulevard");
1715    doc.add_paragraph("Austin, TX 78701");
1716
1717    doc.add_paragraph("");
1718
1719    // ── Subject ──
1720    {
1721        let mut p = doc.add_paragraph("");
1722        p.add_run("Re: Strategic Technology Partnership — Phase 2 Expansion")
1723            .bold(true);
1724    }
1725
1726    doc.add_paragraph("");
1727
1728    // ── Body ──
1729    doc.add_paragraph("Dear Ms. Chen,");
1730    doc.add_paragraph("");
1731
1732    doc.add_paragraph(
1733        "I am writing to express Tensorbee's enthusiasm for expanding our strategic technology \
1734         partnership with Meridian Dynamics. Following the successful completion of Phase 1, \
1735         which delivered a 40% improvement in your ML inference pipeline throughput, we believe \
1736         the foundation is firmly established for an ambitious Phase 2 engagement.",
1737    )
1738    .first_line_indent(Length::inches(0.5));
1739
1740    doc.add_paragraph(
1741        "During our recent executive review, your team highlighted three priority areas for \
1742         the next phase of collaboration:",
1743    )
1744    .first_line_indent(Length::inches(0.5));
1745
1746    doc.add_numbered_list_item(
1747        "Real-time anomaly detection for your financial transaction monitoring system, \
1748         targeting sub-100ms latency at 50,000 transactions per second.",
1749        0,
1750    );
1751    doc.add_numbered_list_item(
1752        "Federated learning infrastructure to enable model training across your distributed \
1753         data centers without centralizing sensitive financial data.",
1754        0,
1755    );
1756    doc.add_numbered_list_item(
1757        "MLOps automation to reduce your model deployment cycle from the current 5 days \
1758         to under 4 hours.",
1759        0,
1760    );
1761
1762    doc.add_paragraph(
1763        "Our engineering team has prepared a detailed technical proposal addressing each of \
1764         these areas. We have allocated a dedicated team of eight senior engineers, led by \
1765         our CTO, to ensure continuity with the Phase 1 team your organization has already \
1766         built a productive working relationship with.",
1767    )
1768    .first_line_indent(Length::inches(0.5));
1769
1770    doc.add_paragraph(
1771        "I would welcome the opportunity to present our Phase 2 proposal in person. My \
1772         assistant will reach out to coordinate a meeting at your convenience during the \
1773         first week of March.",
1774    )
1775    .first_line_indent(Length::inches(0.5));
1776
1777    doc.add_paragraph("");
1778
1779    doc.add_paragraph("Warm regards,");
1780    doc.add_paragraph("");
1781    doc.add_paragraph("");
1782
1783    // Signature line
1784    doc.add_paragraph("")
1785        .border_bottom(BorderStyle::Single, 4, "4A5568");
1786    {
1787        let mut p = doc.add_paragraph("");
1788        p.add_run("Walter White").bold(true).size(12.0);
1789    }
1790    {
1791        let mut p = doc.add_paragraph("");
1792        p.add_run("Chief Executive Officer, Tensorbee")
1793            .italic(true)
1794            .size(10.0)
1795            .color("666666");
1796    }
1797    {
1798        let mut p = doc.add_paragraph("");
1799        p.add_run("walter@tensorbee.com | +1 (415) 555-0199")
1800            .size(10.0)
1801            .color("888888");
1802    }
1803
1804    doc
1805}
1806
1807// =============================================================================
1808// 7. EMPLOYMENT CONTRACT — Purple + charcoal scheme
1809// =============================================================================
1810fn generate_contract(_samples_dir: &Path) -> Document {
1811    let mut doc = Document::new();
1812
1813    // Colors: Purple #5B2C6F, Plum #8E44AD, Gray #2C3E50
1814    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1815    doc.set_margins(
1816        Length::inches(1.25),
1817        Length::inches(1.0),
1818        Length::inches(1.0),
1819        Length::inches(1.0),
1820    );
1821    doc.set_title("Employment Agreement");
1822    doc.set_author("Tensorbee HR Department");
1823    doc.set_subject("Employment Contract — Walter White");
1824    doc.set_keywords("employment, contract, agreement, Tensorbee");
1825
1826    doc.set_header("TENSORBEE — Employment Agreement");
1827    doc.set_footer("Employment Agreement — Walter White — Page");
1828
1829    // ── Title Block ──
1830    doc.add_paragraph("")
1831        .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1832    {
1833        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1834        p.add_run("EMPLOYMENT AGREEMENT")
1835            .bold(true)
1836            .size(24.0)
1837            .color("5B2C6F")
1838            .font("Georgia");
1839    }
1840    doc.add_paragraph("")
1841        .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1842    doc.add_paragraph("");
1843
1844    // ── Parties ──
1845    doc.add_paragraph(
1846        "This Employment Agreement (\"Agreement\") is entered into as of February 22, 2026 \
1847         (\"Effective Date\"), by and between:",
1848    );
1849
1850    doc.add_paragraph("");
1851
1852    {
1853        let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1854        p.add_run("EMPLOYER: ").bold(true);
1855        p.add_run(
1856            "Tensorbee, Inc., a Delaware corporation with its principal place of business at \
1857                   123 Innovation Drive, Suite 400, San Francisco, CA 94105 (\"Company\")",
1858        );
1859    }
1860    doc.add_paragraph("");
1861    {
1862        let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1863        p.add_run("EMPLOYEE: ").bold(true);
1864        p.add_run(
1865            "Walter White, residing at 308 Negra Arroyo Lane, Albuquerque, NM 87104 (\"Employee\")",
1866        );
1867    }
1868
1869    doc.add_paragraph("");
1870    doc.add_paragraph("The Company and Employee are collectively referred to as the \"Parties.\"");
1871    doc.add_paragraph("");
1872
1873    // ── Article 1: Position and Duties ──
1874    doc.add_paragraph("Article 1 — Position and Duties")
1875        .style("Heading1");
1876    {
1877        let mut p = doc.add_paragraph("");
1878        p.add_run("1.1 ").bold(true);
1879        p.add_run("The Company hereby employs the Employee as ");
1880        p.add_run("Chief Executive Officer (CEO)")
1881            .bold(true)
1882            .italic(true);
1883        p.add_run(", reporting directly to the Board of Directors.");
1884    }
1885    {
1886        let mut p = doc.add_paragraph("");
1887        p.add_run("1.2 ").bold(true);
1888        p.add_run("The Employee shall devote full working time, attention, and best efforts to \
1889                   the performance of duties as reasonably assigned by the Board, including but not limited to:");
1890    }
1891    doc.add_bullet_list_item(
1892        "Setting and executing the Company's strategic vision and business plan",
1893        0,
1894    );
1895    doc.add_bullet_list_item(
1896        "Overseeing all operations, engineering, and commercial activities",
1897        0,
1898    );
1899    doc.add_bullet_list_item(
1900        "Representing the Company to investors, customers, and the public",
1901        0,
1902    );
1903    doc.add_bullet_list_item("Recruiting, developing, and retaining key talent", 0);
1904
1905    {
1906        let mut p = doc.add_paragraph("");
1907        p.add_run("1.3 ").bold(true);
1908        p.add_run(
1909            "The Employee's primary work location shall be the Company's San Francisco \
1910                   headquarters, with reasonable travel as required by business needs.",
1911        );
1912    }
1913
1914    // ── Article 2: Compensation ──
1915    doc.add_paragraph("Article 2 — Compensation and Benefits")
1916        .style("Heading1");
1917    {
1918        let mut p = doc.add_paragraph("");
1919        p.add_run("2.1 Base Salary. ").bold(true);
1920        p.add_run("The Company shall pay the Employee an annual base salary of ");
1921        p.add_run("$375,000.00").bold(true);
1922        p.add_run(" (Three Hundred Seventy-Five Thousand Dollars), payable in accordance with the \
1923                   Company's standard payroll schedule, less applicable withholdings and deductions.");
1924    }
1925    {
1926        let mut p = doc.add_paragraph("");
1927        p.add_run("2.2 Annual Bonus. ").bold(true);
1928        p.add_run("The Employee shall be eligible for an annual performance bonus of up to ");
1929        p.add_run("40%").bold(true);
1930        p.add_run(
1931            " of base salary, based on achievement of mutually agreed performance objectives.",
1932        );
1933    }
1934    {
1935        let mut p = doc.add_paragraph("");
1936        p.add_run("2.3 Equity. ").bold(true);
1937        p.add_run("Subject to Board approval, the Employee shall receive a stock option grant of ");
1938        p.add_run("500,000 shares").bold(true);
1939        p.add_run(
1940            " of the Company's common stock, vesting over four (4) years with a one-year cliff, \
1941                   at an exercise price equal to the fair market value on the date of grant.",
1942        );
1943    }
1944
1945    // Compensation summary table
1946    doc.add_paragraph("");
1947    {
1948        let mut tbl = doc
1949            .add_table(5, 2)
1950            .borders(BorderStyle::Single, 4, "5B2C6F")
1951            .width_pct(70.0)
1952            .alignment(Alignment::Center);
1953        tbl.cell(0, 0).unwrap().set_text("Compensation Element");
1954        tbl.cell(0, 0).unwrap().shading("5B2C6F");
1955        tbl.cell(0, 1).unwrap().set_text("Value");
1956        tbl.cell(0, 1).unwrap().shading("5B2C6F");
1957        let items = [
1958            ("Base Salary", "$375,000/year"),
1959            ("Target Bonus", "Up to 40% ($150,000)"),
1960            ("Equity Grant", "500,000 shares (4yr vest)"),
1961            ("Total Target Comp", "$525,000 + equity"),
1962        ];
1963        for (i, (elem, val)) in items.iter().enumerate() {
1964            tbl.cell(i + 1, 0).unwrap().set_text(elem);
1965            tbl.cell(i + 1, 1).unwrap().set_text(val);
1966            if i == 3 {
1967                tbl.cell(i + 1, 0).unwrap().shading("E8DAEF");
1968                tbl.cell(i + 1, 1).unwrap().shading("E8DAEF");
1969            }
1970        }
1971    }
1972
1973    // ── Article 3: Benefits ──
1974    doc.add_paragraph("").page_break_before(true);
1975    doc.add_paragraph("Article 3 — Benefits").style("Heading1");
1976    {
1977        let mut p = doc.add_paragraph("");
1978        p.add_run("3.1 ").bold(true);
1979        p.add_run(
1980            "The Employee shall be entitled to participate in all benefit programs \
1981                   generally available to senior executives, including:",
1982        );
1983    }
1984    doc.add_bullet_list_item(
1985        "Medical, dental, and vision insurance (100% premium coverage for employee and dependents)",
1986        0,
1987    );
1988    doc.add_bullet_list_item("401(k) retirement plan with 6% company match", 0);
1989    doc.add_bullet_list_item("Life insurance and long-term disability coverage", 0);
1990    doc.add_bullet_list_item("Annual professional development allowance of $10,000", 0);
1991
1992    {
1993        let mut p = doc.add_paragraph("");
1994        p.add_run("3.2 Paid Time Off. ").bold(true);
1995        p.add_run(
1996            "The Employee shall receive 25 days of paid vacation per year, plus Company holidays, \
1997                   accruing on a monthly basis.",
1998        );
1999    }
2000
2001    // ── Article 4: Term and Termination ──
2002    doc.add_paragraph("Article 4 — Term and Termination")
2003        .style("Heading1");
2004    {
2005        let mut p = doc.add_paragraph("");
2006        p.add_run("4.1 At-Will Employment. ").bold(true);
2007        p.add_run(
2008            "This Agreement is for at-will employment and may be terminated by either Party \
2009                   at any time, with or without cause, subject to the notice provisions herein.",
2010        );
2011    }
2012    {
2013        let mut p = doc.add_paragraph("");
2014        p.add_run("4.2 Notice Period. ").bold(true);
2015        p.add_run("Either Party shall provide ");
2016        p.add_run("ninety (90) days").bold(true);
2017        p.add_run(" written notice of termination, or pay in lieu thereof.");
2018    }
2019    {
2020        let mut p = doc.add_paragraph("");
2021        p.add_run("4.3 Severance. ").bold(true);
2022        p.add_run("In the event of termination by the Company without Cause, the Employee shall \
2023                   receive (a) twelve (12) months of base salary continuation, (b) pro-rata bonus \
2024                   for the year of termination, and (c) twelve (12) months of COBRA premium coverage.");
2025    }
2026
2027    // ── Article 5: Confidentiality ──
2028    doc.add_paragraph("Article 5 — Confidentiality and IP")
2029        .style("Heading1");
2030    {
2031        let mut p = doc.add_paragraph("");
2032        p.add_run("5.1 ").bold(true);
2033        p.add_run(
2034            "The Employee agrees to maintain strict confidentiality of all proprietary \
2035                   information, trade secrets, and business plans of the Company during and after \
2036                   employment.",
2037        );
2038    }
2039    {
2040        let mut p = doc.add_paragraph("");
2041        p.add_run("5.2 ").bold(true);
2042        p.add_run(
2043            "All intellectual property, inventions, and works of authorship created by the \
2044                   Employee during the term of employment and within the scope of duties shall be \
2045                   the sole and exclusive property of the Company.",
2046        );
2047    }
2048
2049    // ── Article 6: Non-Compete ──
2050    doc.add_paragraph("Article 6 — Non-Competition")
2051        .style("Heading1");
2052    {
2053        let mut p = doc.add_paragraph("");
2054        p.add_run("6.1 ").bold(true);
2055        p.add_run("For a period of twelve (12) months following termination, the Employee agrees \
2056                   not to engage in any business that directly competes with the Company's core \
2057                   business of AI infrastructure and ML operations services, within the United States.");
2058    }
2059
2060    // ── Article 7: General Provisions ──
2061    doc.add_paragraph("Article 7 — General Provisions")
2062        .style("Heading1");
2063    {
2064        let mut p = doc.add_paragraph("");
2065        p.add_run("7.1 Governing Law. ").bold(true);
2066        p.add_run(
2067            "This Agreement shall be governed by and construed in accordance with the laws of \
2068                   the State of California.",
2069        );
2070    }
2071    {
2072        let mut p = doc.add_paragraph("");
2073        p.add_run("7.2 Entire Agreement. ").bold(true);
2074        p.add_run(
2075            "This Agreement constitutes the entire agreement between the Parties and supersedes \
2076                   all prior negotiations, representations, and agreements.",
2077        );
2078    }
2079    {
2080        let mut p = doc.add_paragraph("");
2081        p.add_run("7.3 Amendment. ").bold(true);
2082        p.add_run(
2083            "This Agreement may only be amended by a written instrument signed by both Parties.",
2084        );
2085    }
2086
2087    // ── Signature Block ──
2088    doc.add_paragraph("").page_break_before(true);
2089    doc.add_paragraph("IN WITNESS WHEREOF, the Parties have executed this Employment Agreement as of the Effective Date.")
2090        .space_after(Length::pt(24.0));
2091
2092    // Signature table
2093    {
2094        let mut tbl = doc.add_table(6, 2).width_pct(100.0);
2095        tbl.cell(0, 0).unwrap().set_text("FOR THE COMPANY:");
2096        tbl.cell(0, 0).unwrap().shading("5B2C6F");
2097        tbl.cell(0, 1).unwrap().set_text("EMPLOYEE:");
2098        tbl.cell(0, 1).unwrap().shading("5B2C6F");
2099
2100        tbl.cell(1, 0).unwrap().set_text("");
2101        tbl.cell(1, 1).unwrap().set_text("");
2102        tbl.row(1).unwrap().height(Length::pt(40.0));
2103
2104        tbl.cell(2, 0)
2105            .unwrap()
2106            .set_text("Signature: ___________________________");
2107        tbl.cell(2, 1)
2108            .unwrap()
2109            .set_text("Signature: ___________________________");
2110        tbl.cell(3, 0)
2111            .unwrap()
2112            .set_text("Name: Board Representative");
2113        tbl.cell(3, 1).unwrap().set_text("Name: Walter White");
2114        tbl.cell(4, 0)
2115            .unwrap()
2116            .set_text("Title: Chair, Board of Directors");
2117        tbl.cell(4, 1)
2118            .unwrap()
2119            .set_text("Title: Chief Executive Officer");
2120        tbl.cell(5, 0).unwrap().set_text("Date: _______________");
2121        tbl.cell(5, 1).unwrap().set_text("Date: _______________");
2122    }
2123
2124    doc
2125}
Source

pub fn vertical_alignment(self, align: VerticalAlignment) -> Self

Set vertical alignment within the cell.

Examples found in repository?
examples/styled_tables.rs (line 177)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
More examples
Hide additional examples
examples/generate_samples.rs (line 363)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 458)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
Source

pub fn grid_span(self, span: u32) -> Self

Set horizontal merge (gridSpan). This cell spans span columns.

Examples found in repository?
examples/styled_tables.rs (line 105)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
More examples
Hide additional examples
examples/generate_samples.rs (line 337)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 383)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
684
685// =============================================================================
686// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
687// =============================================================================
688fn generate_proposal(_samples_dir: &Path) -> Document {
689    let mut doc = Document::new();
690
691    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
692    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
693    doc.set_margins(
694        Length::inches(1.0),
695        Length::inches(1.0),
696        Length::inches(1.0),
697        Length::inches(1.0),
698    );
699    doc.set_title("AI Platform Modernization Proposal");
700    doc.set_author("Walter White");
701    doc.set_subject("Technology Proposal");
702    doc.set_keywords("proposal, AI, modernization, Tensorbee");
703
704    // Banner header
705    let logo_img = create_logo_png(220, 48);
706    let banner = build_header_banner_xml(
707        "rId1",
708        &BannerOpts {
709            bg_color: "1B2A4A",
710            banner_width: 7772400,
711            banner_height: 914400,
712            logo_width: 2011680,
713            logo_height: 438912,
714            logo_x_offset: 295125,
715            logo_y_offset: 237744,
716        },
717    );
718    doc.set_raw_header_with_images(
719        banner,
720        &[("rId1", &logo_img, "logo.png")],
721        rdocx_oxml::header_footer::HdrFtrType::Default,
722    );
723    doc.set_different_first_page(true);
724    let cover_banner = build_header_banner_xml(
725        "rId1",
726        &BannerOpts {
727            bg_color: "C5922E",
728            banner_width: 7772400,
729            banner_height: 914400,
730            logo_width: 2011680,
731            logo_height: 438912,
732            logo_x_offset: 295125,
733            logo_y_offset: 237744,
734        },
735    );
736    doc.set_raw_header_with_images(
737        cover_banner,
738        &[("rId1", &logo_img, "logo.png")],
739        rdocx_oxml::header_footer::HdrFtrType::First,
740    );
741    doc.set_margins(
742        Length::twips(2292),
743        Length::twips(1440),
744        Length::twips(1440),
745        Length::twips(1440),
746    );
747    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
748    doc.set_footer("Tensorbee — Confidential");
749
750    // Custom styles
751    doc.add_style(
752        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
753            .based_on("Normal")
754            .run_properties({
755                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
756                rpr.bold = Some(true);
757                rpr.font_ascii = Some("Georgia".to_string());
758                rpr.font_hansi = Some("Georgia".to_string());
759                rpr.sz = Some(rdocx_oxml::HalfPoint(56)); // half-points → 28pt
760                rpr.color = Some("1B2A4A".to_string());
761                rpr
762            }),
763    );
764
765    // ── Cover Page ──
766    for _ in 0..4 {
767        doc.add_paragraph("");
768    }
769    doc.add_paragraph("AI Platform Modernization")
770        .style("ProposalTitle")
771        .alignment(Alignment::Center);
772    doc.add_paragraph("")
773        .alignment(Alignment::Center)
774        .border_bottom(BorderStyle::Single, 8, "C5922E");
775    {
776        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
777        p.add_run("Prepared for Global Financial Corp.")
778            .size(14.0)
779            .color("666666")
780            .italic(true);
781    }
782    {
783        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
784        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
785            .size(12.0)
786            .color("888888");
787    }
788    {
789        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
790        p.add_run("February 22, 2026").size(12.0).color("888888");
791    }
792
793    // ── TOC ──
794    doc.add_paragraph("").page_break_before(true);
795    doc.add_paragraph("Table of Contents").style("Heading1");
796    // We'll add a manual-style TOC since the insert_toc goes at a specific position
797    doc.insert_toc(doc.content_count(), 2);
798
799    // ── Executive Summary ──
800    doc.add_paragraph("").page_break_before(true);
801    doc.add_paragraph("Executive Summary").style("Heading1");
802    doc.add_paragraph(
803        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
804         transitioning from legacy batch-processing systems to a real-time inference architecture. \
805         This transformation will reduce model deployment time from weeks to hours, improve \
806         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
807    )
808    .first_line_indent(Length::inches(0.3));
809    doc.add_paragraph(
810        "The project spans three phases over 18 months, with a total investment of $2.8M. \
811         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
812         similar transformations for three Fortune 500 financial institutions.",
813    )
814    .first_line_indent(Length::inches(0.3));
815
816    // Key metrics table
817    doc.add_paragraph("");
818    {
819        let mut tbl = doc
820            .add_table(5, 2)
821            .borders(BorderStyle::Single, 4, "1B2A4A")
822            .width_pct(80.0);
823        tbl.cell(0, 0).unwrap().set_text("Key Metric");
824        tbl.cell(0, 0).unwrap().shading("1B2A4A");
825        tbl.cell(0, 1).unwrap().set_text("Value");
826        tbl.cell(0, 1).unwrap().shading("1B2A4A");
827        let rows = [
828            ("Total Investment", "$2.8M"),
829            ("Annual Savings", "$4.2M"),
830            ("ROI (Year 1)", "150%"),
831            ("Timeline", "18 months"),
832        ];
833        for (i, (k, v)) in rows.iter().enumerate() {
834            tbl.cell(i + 1, 0).unwrap().set_text(k);
835            if i % 2 == 0 {
836                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
837            }
838            tbl.cell(i + 1, 1).unwrap().set_text(v);
839            if i % 2 == 0 {
840                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
841            }
842        }
843    }
844
845    // ── Problem Statement ──
846    doc.add_paragraph("").page_break_before(true);
847    doc.add_paragraph("Problem Statement").style("Heading1");
848    doc.add_paragraph(
849        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
850    );
851    doc.add_numbered_list_item("Deployment Latency — New models take 3-4 weeks to deploy to production, compared to the industry benchmark of 2-3 days.", 0);
852    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
853    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
854
855    // ── Proposed Solution ──
856    doc.add_paragraph("").page_break_before(true);
857    doc.add_paragraph("Proposed Solution").style("Heading1");
858
859    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
860        .style("Heading2");
861    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
862    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
863    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
864
865    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
866        .style("Heading2");
867    doc.add_bullet_list_item(
868        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
869        0,
870    );
871    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
872    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
873
874    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
875        .style("Heading2");
876    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
877    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
878    doc.add_bullet_list_item(
879        "Self-service model deployment portal for data science teams",
880        0,
881    );
882
883    // ── Budget ──
884    doc.add_paragraph("Budget Breakdown").style("Heading1");
885    {
886        let mut tbl = doc
887            .add_table(6, 3)
888            .borders(BorderStyle::Single, 4, "1B2A4A")
889            .width_pct(100.0);
890        tbl.cell(0, 0).unwrap().set_text("Category");
891        tbl.cell(0, 0).unwrap().shading("1B2A4A");
892        tbl.cell(0, 1).unwrap().set_text("Cost");
893        tbl.cell(0, 1).unwrap().shading("1B2A4A");
894        tbl.cell(0, 2).unwrap().set_text("Notes");
895        tbl.cell(0, 2).unwrap().shading("1B2A4A");
896        let items = [
897            (
898                "Infrastructure",
899                "$450,000",
900                "Cloud compute + GPU instances",
901            ),
902            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
903            (
904                "Software Licenses",
905                "$350,000",
906                "Kafka, monitoring, CI/CD tools",
907            ),
908            (
909                "Training & Enablement",
910                "$200,000",
911                "Team training, documentation",
912            ),
913            ("Contingency (10%)", "$200,000", "Risk buffer"),
914        ];
915        for (i, (cat, cost, note)) in items.iter().enumerate() {
916            tbl.cell(i + 1, 0).unwrap().set_text(cat);
917            tbl.cell(i + 1, 1).unwrap().set_text(cost);
918            tbl.cell(i + 1, 2).unwrap().set_text(note);
919            if i % 2 == 0 {
920                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
921                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
922                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
923            }
924        }
925    }
926
927    // ── Closing ──
928    doc.add_paragraph("");
929    doc.add_paragraph("Next Steps").style("Heading1");
930    doc.add_numbered_list_item(
931        "Schedule technical deep-dive with GFC engineering team (Week 1)",
932        0,
933    );
934    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
935    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
936
937    doc.add_paragraph("");
938    {
939        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
940        p.add_run("Walter White")
941            .bold(true)
942            .size(14.0)
943            .color("1B2A4A");
944    }
945    {
946        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
947        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
948            .size(10.0)
949            .color("888888");
950    }
951
952    doc
953}
954
955// =============================================================================
956// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
957// =============================================================================
958fn generate_quote(_samples_dir: &Path) -> Document {
959    let mut doc = Document::new();
960
961    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
962    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
963    doc.set_margins(
964        Length::inches(0.75),
965        Length::inches(0.75),
966        Length::inches(0.75),
967        Length::inches(0.75),
968    );
969    doc.set_title("Quotation QT-2026-0147");
970    doc.set_author("Walter White");
971    doc.set_keywords("quote, BOM, Tensorbee");
972
973    doc.set_header("Tensorbee — Quotation");
974    doc.set_footer("QT-2026-0147 | Page");
975
976    // ── Header Block ──
977    {
978        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
979        p.add_run("TENSORBEE")
980            .bold(true)
981            .size(28.0)
982            .color("008B8B")
983            .font("Helvetica");
984    }
985    doc.add_paragraph("123 Innovation Drive, Suite 400")
986        .alignment(Alignment::Left);
987    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
988        .alignment(Alignment::Left);
989    doc.add_paragraph("")
990        .border_bottom(BorderStyle::Single, 8, "008B8B");
991
992    // Quote meta
993    doc.add_paragraph("");
994    {
995        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
996        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
997        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
998        tbl.cell(0, 2).unwrap().set_text("DATE");
999        tbl.cell(0, 2).unwrap().shading("008B8B");
1000        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1001        tbl.cell(0, 3).unwrap().shading("008B8B");
1002        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1003        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1004        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1005        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1006        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1007        tbl.cell(2, 1).unwrap().set_text("Walter White");
1008        tbl.cell(2, 2).unwrap().set_text("Payment:");
1009        tbl.cell(2, 3).unwrap().set_text("Net 30");
1010        tbl.cell(3, 0).unwrap().set_text("Customer:");
1011        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1012        tbl.cell(3, 2).unwrap().set_text("Currency:");
1013        tbl.cell(3, 3).unwrap().set_text("USD");
1014    }
1015
1016    doc.add_paragraph("");
1017
1018    // ── Bill of Materials ──
1019    {
1020        let mut p = doc.add_paragraph("");
1021        p.add_run("Bill of Materials")
1022            .bold(true)
1023            .size(16.0)
1024            .color("1A3C3C");
1025    }
1026    doc.add_paragraph("");
1027
1028    {
1029        let mut tbl = doc
1030            .add_table(10, 6)
1031            .borders(BorderStyle::Single, 2, "008B8B")
1032            .width_pct(100.0)
1033            .layout_fixed();
1034        // Headers
1035        let hdrs = [
1036            "#",
1037            "Part Number",
1038            "Description",
1039            "Qty",
1040            "Unit Price",
1041            "Total",
1042        ];
1043        for (c, h) in hdrs.iter().enumerate() {
1044            tbl.cell(0, c).unwrap().set_text(h);
1045            tbl.cell(0, c).unwrap().shading("008B8B");
1046        }
1047
1048        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1049            (
1050                "TB-GPU-A100",
1051                "NVIDIA A100 80GB GPU",
1052                "4",
1053                "$12,500.00",
1054                "$50,000.00",
1055            ),
1056            (
1057                "TB-SRV-R750",
1058                "Dell R750xa Server Chassis",
1059                "2",
1060                "$8,200.00",
1061                "$16,400.00",
1062            ),
1063            (
1064                "TB-RAM-256G",
1065                "256GB DDR5 ECC Memory Module",
1066                "8",
1067                "$890.00",
1068                "$7,120.00",
1069            ),
1070            (
1071                "TB-SSD-3840",
1072                "3.84TB NVMe U.2 SSD",
1073                "8",
1074                "$1,150.00",
1075                "$9,200.00",
1076            ),
1077            (
1078                "TB-NET-CX7",
1079                "ConnectX-7 200GbE NIC",
1080                "4",
1081                "$1,800.00",
1082                "$7,200.00",
1083            ),
1084            (
1085                "TB-SW-48P",
1086                "48-Port 100GbE Switch",
1087                "1",
1088                "$22,000.00",
1089                "$22,000.00",
1090            ),
1091            (
1092                "TB-CAB-RACK",
1093                "42U Server Rack + PDU",
1094                "1",
1095                "$4,500.00",
1096                "$4,500.00",
1097            ),
1098            (
1099                "TB-SVC-INST",
1100                "Installation & Configuration",
1101                "1",
1102                "$8,500.00",
1103                "$8,500.00",
1104            ),
1105            (
1106                "TB-SVC-SUPP",
1107                "3-Year Premium Support",
1108                "1",
1109                "$15,000.00",
1110                "$15,000.00",
1111            ),
1112        ];
1113
1114        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1115            let row = i + 1;
1116            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1117            tbl.cell(row, 1).unwrap().set_text(pn);
1118            tbl.cell(row, 2).unwrap().set_text(desc);
1119            tbl.cell(row, 3).unwrap().set_text(qty);
1120            tbl.cell(row, 4).unwrap().set_text(unit);
1121            tbl.cell(row, 5).unwrap().set_text(total);
1122            if i % 2 == 0 {
1123                for c in 0..6 {
1124                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1125                }
1126            }
1127        }
1128    }
1129
1130    doc.add_paragraph("");
1131
1132    // ── Totals ──
1133    {
1134        let mut tbl = doc
1135            .add_table(4, 2)
1136            .borders(BorderStyle::Single, 2, "008B8B")
1137            .width(Length::inches(3.5))
1138            .alignment(Alignment::Right);
1139        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1140        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1141        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1142        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1143        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1144        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1145        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1146        tbl.cell(3, 0).unwrap().shading("E07020");
1147        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1148        tbl.cell(3, 1).unwrap().shading("E07020");
1149    }
1150
1151    doc.add_paragraph("");
1152
1153    // ── Terms & Conditions ──
1154    {
1155        let mut p = doc.add_paragraph("");
1156        p.add_run("Terms & Conditions")
1157            .bold(true)
1158            .size(14.0)
1159            .color("1A3C3C");
1160    }
1161    doc.add_numbered_list_item(
1162        "This quotation is valid for 30 calendar days from the date of issue.",
1163        0,
1164    );
1165    doc.add_numbered_list_item(
1166        "All prices are in USD and exclusive of applicable taxes unless stated.",
1167        0,
1168    );
1169    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1170    doc.add_numbered_list_item(
1171        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1172        0,
1173    );
1174    doc.add_numbered_list_item(
1175        "Warranty: 3-year manufacturer warranty on all hardware components.",
1176        0,
1177    );
1178    doc.add_numbered_list_item(
1179        "Returns subject to 15% restocking fee if initiated after 14 days.",
1180        0,
1181    );
1182
1183    doc.add_paragraph("");
1184    doc.add_paragraph("")
1185        .border_bottom(BorderStyle::Single, 4, "008B8B");
1186    doc.add_paragraph("");
1187    {
1188        let mut p = doc.add_paragraph("");
1189        p.add_run("Accepted by: ").bold(true);
1190        p.add_run("___________________________ Date: ___________");
1191    }
1192    {
1193        let mut p = doc.add_paragraph("");
1194        p.add_run("Print Name: ").bold(true);
1195        p.add_run("___________________________ Title: ___________");
1196    }
1197
1198    doc
1199}
Source

pub fn v_merge_restart(self) -> Self

Start a vertical merge group (this cell is the top of the merged range).

Examples found in repository?
examples/styled_tables.rs (line 173)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
More examples
Hide additional examples
examples/generate_samples.rs (line 388)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 412)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
Source

pub fn v_merge_continue(self) -> Self

Continue a vertical merge group (this cell merges with the one above).

Examples found in repository?
examples/styled_tables.rs (line 181)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
More examples
Hide additional examples
examples/generate_samples.rs (line 392)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 415)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
Source

pub fn no_wrap(self) -> Self

Set no-wrap for text in this cell.

Examples found in repository?
examples/generate_all_samples.rs (line 472)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}
Source

pub fn add_table(&mut self, rows: usize, cols: usize) -> Table<'_>

Add a nested table inside this cell.

Examples found in repository?
examples/styled_tables.rs (line 245)
24fn generate_styled_tables(path: &Path) {
25    let mut doc = Document::new();
26    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27    doc.set_margins(
28        Length::inches(0.75),
29        Length::inches(0.75),
30        Length::inches(0.75),
31        Length::inches(0.75),
32    );
33
34    doc.add_paragraph("Styled Tables Showcase")
35        .style("Heading1");
36
37    doc.add_paragraph("");
38
39    // =========================================================================
40    // 1. Professional report table with alternating rows
41    // =========================================================================
42    doc.add_paragraph("1. Report Table with Alternating Row Colors")
43        .style("Heading2");
44
45    {
46        let mut tbl = doc.add_table(8, 4);
47        tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48        tbl = tbl.width_pct(100.0);
49
50        // Header row
51        let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52        for (col, h) in headers.iter().enumerate() {
53            tbl.cell(0, col).unwrap().shading("2E75B6");
54            tbl.cell(0, col).unwrap().set_text(h);
55        }
56        tbl.row(0).unwrap().header();
57
58        // Data with alternating shading
59        let data = [
60            ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61            ["Professional", "$189,000", "$201,000", "+6.3%"],
62            ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63            ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64            ["Training", "$22,000", "$28,900", "+31.4%"],
65            ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66        ];
67
68        for (i, row) in data.iter().enumerate() {
69            let row_idx = i + 1;
70            for (col, val) in row.iter().enumerate() {
71                tbl.cell(row_idx, col).unwrap().set_text(val);
72                // Alternate row colors
73                if i % 2 == 0 {
74                    tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75                }
76            }
77        }
78
79        // Total row
80        tbl.cell(7, 0).unwrap().set_text("TOTAL");
81        tbl.cell(7, 0).unwrap().shading("D6E4F0");
82        tbl.cell(7, 1).unwrap().set_text("$613,000");
83        tbl.cell(7, 1).unwrap().shading("D6E4F0");
84        tbl.cell(7, 2).unwrap().set_text("$729,900");
85        tbl.cell(7, 2).unwrap().shading("D6E4F0");
86        tbl.cell(7, 3).unwrap().set_text("+19.1%");
87        tbl.cell(7, 3).unwrap().shading("D6E4F0");
88    }
89
90    doc.add_paragraph("");
91
92    // =========================================================================
93    // 2. Invoice-style table with merged header
94    // =========================================================================
95    doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96        .style("Heading2");
97
98    {
99        let mut tbl = doc.add_table(7, 4);
100        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101        tbl = tbl.width_pct(100.0);
102
103        // Merged title row
104        tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105        tbl.cell(0, 0).unwrap().grid_span(4);
106        tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108        // Column headers
109        let headers = ["Item", "Description", "Qty", "Amount"];
110        for (col, h) in headers.iter().enumerate() {
111            tbl.cell(1, col).unwrap().set_text(h);
112            tbl.cell(1, col).unwrap().shading("D6E4F0");
113        }
114
115        // Line items
116        tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117        tbl.cell(2, 1)
118            .unwrap()
119            .set_text("Enterprise License (500 seats)");
120        tbl.cell(2, 2).unwrap().set_text("1");
121        tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123        tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124        tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125        tbl.cell(3, 2).unwrap().set_text("1");
126        tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128        tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129        tbl.cell(4, 1)
130            .unwrap()
131            .set_text("On-site Training (3 days)");
132        tbl.cell(4, 2).unwrap().set_text("1");
133        tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135        // Subtotal
136        tbl.cell(5, 0).unwrap().set_text("Subtotal");
137        tbl.cell(5, 0).unwrap().grid_span(3);
138        tbl.cell(5, 0).unwrap().shading("F2F2F2");
139        tbl.cell(5, 3).unwrap().set_text("$89,500");
140        tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142        // Total
143        tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144        tbl.cell(6, 0).unwrap().grid_span(3);
145        tbl.cell(6, 0).unwrap().shading("1F4E79");
146        tbl.cell(6, 3).unwrap().set_text("$89,500");
147        tbl.cell(6, 3).unwrap().shading("1F4E79");
148    }
149
150    doc.add_paragraph("");
151
152    // =========================================================================
153    // 3. Specification table with vertical merge
154    // =========================================================================
155    doc.add_paragraph("3. Specification Table with Vertical Merges")
156        .style("Heading2");
157
158    {
159        let mut tbl = doc.add_table(8, 3);
160        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161        tbl = tbl.width_pct(100.0);
162
163        // Header
164        tbl.cell(0, 0).unwrap().set_text("Category");
165        tbl.cell(0, 0).unwrap().shading("2E75B6");
166        tbl.cell(0, 1).unwrap().set_text("Specification");
167        tbl.cell(0, 1).unwrap().shading("2E75B6");
168        tbl.cell(0, 2).unwrap().set_text("Value");
169        tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171        // "Hardware" spans 3 rows
172        tbl.cell(1, 0).unwrap().set_text("Hardware");
173        tbl.cell(1, 0).unwrap().v_merge_restart();
174        tbl.cell(1, 0).unwrap().shading("E2EFDA");
175        tbl.cell(1, 0)
176            .unwrap()
177            .vertical_alignment(VerticalAlignment::Center);
178        tbl.cell(1, 1).unwrap().set_text("Processor");
179        tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181        tbl.cell(2, 0).unwrap().v_merge_continue();
182        tbl.cell(2, 1).unwrap().set_text("Memory");
183        tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185        tbl.cell(3, 0).unwrap().v_merge_continue();
186        tbl.cell(3, 1).unwrap().set_text("Storage");
187        tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189        // "Network" spans 2 rows
190        tbl.cell(4, 0).unwrap().set_text("Network");
191        tbl.cell(4, 0).unwrap().v_merge_restart();
192        tbl.cell(4, 0).unwrap().shading("FCE4D6");
193        tbl.cell(4, 0)
194            .unwrap()
195            .vertical_alignment(VerticalAlignment::Center);
196        tbl.cell(4, 1).unwrap().set_text("Ethernet");
197        tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199        tbl.cell(5, 0).unwrap().v_merge_continue();
200        tbl.cell(5, 1).unwrap().set_text("Management");
201        tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203        // "Software" spans 2 rows
204        tbl.cell(6, 0).unwrap().set_text("Software");
205        tbl.cell(6, 0).unwrap().v_merge_restart();
206        tbl.cell(6, 0).unwrap().shading("D6E4F0");
207        tbl.cell(6, 0)
208            .unwrap()
209            .vertical_alignment(VerticalAlignment::Center);
210        tbl.cell(6, 1).unwrap().set_text("Operating System");
211        tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213        tbl.cell(7, 0).unwrap().v_merge_continue();
214        tbl.cell(7, 1).unwrap().set_text("Monitoring");
215        tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216    }
217
218    doc.add_paragraph("");
219
220    // =========================================================================
221    // 4. Nested table (table inside a cell)
222    // =========================================================================
223    doc.add_paragraph("4. Nested Table").style("Heading2");
224
225    {
226        let mut tbl = doc.add_table(2, 2);
227        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228        tbl = tbl.width_pct(100.0);
229        tbl = tbl.cell_margins(
230            Length::twips(72),
231            Length::twips(108),
232            Length::twips(72),
233            Length::twips(108),
234        );
235
236        tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237        tbl.cell(0, 0).unwrap().shading("2E75B6");
238        tbl.cell(0, 1).unwrap().set_text("Project Beta");
239        tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241        // Nested table in cell (1,0)
242        {
243            let mut cell = tbl.cell(1, 0).unwrap();
244            cell.set_text("Milestones:");
245            let mut inner = cell.add_table(3, 2);
246            inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247            inner.cell(0, 0).unwrap().set_text("Phase");
248            inner.cell(0, 0).unwrap().shading("E2EFDA");
249            inner.cell(0, 1).unwrap().set_text("Status");
250            inner.cell(0, 1).unwrap().shading("E2EFDA");
251            inner.cell(1, 0).unwrap().set_text("Design");
252            inner.cell(1, 1).unwrap().set_text("Complete");
253            inner.cell(2, 0).unwrap().set_text("Build");
254            inner.cell(2, 1).unwrap().set_text("In Progress");
255        }
256
257        // Nested table in cell (1,1)
258        {
259            let mut cell = tbl.cell(1, 1).unwrap();
260            cell.set_text("Budget:");
261            let mut inner = cell.add_table(3, 2);
262            inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263            inner.cell(0, 0).unwrap().set_text("Category");
264            inner.cell(0, 0).unwrap().shading("FCE4D6");
265            inner.cell(0, 1).unwrap().set_text("Amount");
266            inner.cell(0, 1).unwrap().shading("FCE4D6");
267            inner.cell(1, 0).unwrap().set_text("Development");
268            inner.cell(1, 1).unwrap().set_text("$120,000");
269            inner.cell(2, 0).unwrap().set_text("Testing");
270            inner.cell(2, 1).unwrap().set_text("$35,000");
271        }
272    }
273
274    doc.add_paragraph("");
275
276    // =========================================================================
277    // 5. Form-style table with labels
278    // =========================================================================
279    doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281    {
282        let mut tbl = doc.add_table(6, 4);
283        tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284        tbl = tbl.width_pct(100.0);
285
286        // Row 0: Full-width title
287        tbl.cell(0, 0)
288            .unwrap()
289            .set_text("Customer Registration Form");
290        tbl.cell(0, 0).unwrap().grid_span(4);
291        tbl.cell(0, 0).unwrap().shading("404040");
292
293        // Row 1: Name fields
294        tbl.cell(1, 0).unwrap().set_text("First Name");
295        tbl.cell(1, 0).unwrap().shading("E8E8E8");
296        tbl.cell(1, 1).unwrap().set_text("John");
297        tbl.cell(1, 2).unwrap().set_text("Last Name");
298        tbl.cell(1, 2).unwrap().shading("E8E8E8");
299        tbl.cell(1, 3).unwrap().set_text("Smith");
300
301        // Row 2: Contact
302        tbl.cell(2, 0).unwrap().set_text("Email");
303        tbl.cell(2, 0).unwrap().shading("E8E8E8");
304        tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305        tbl.cell(2, 1).unwrap().grid_span(3);
306
307        // Row 3: Phone
308        tbl.cell(3, 0).unwrap().set_text("Phone");
309        tbl.cell(3, 0).unwrap().shading("E8E8E8");
310        tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311        tbl.cell(3, 2).unwrap().set_text("Company");
312        tbl.cell(3, 2).unwrap().shading("E8E8E8");
313        tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315        // Row 4: Address (spanning)
316        tbl.cell(4, 0).unwrap().set_text("Address");
317        tbl.cell(4, 0).unwrap().shading("E8E8E8");
318        tbl.cell(4, 1)
319            .unwrap()
320            .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321        tbl.cell(4, 1).unwrap().grid_span(3);
322
323        // Row 5: Notes
324        tbl.cell(5, 0).unwrap().set_text("Notes");
325        tbl.cell(5, 0).unwrap().shading("E8E8E8");
326        tbl.cell(5, 0)
327            .unwrap()
328            .vertical_alignment(VerticalAlignment::Top);
329        {
330            let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331            cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332            cell.add_paragraph("Annual review scheduled for March 2026.");
333        }
334    }
335
336    doc.add_paragraph("");
337
338    // =========================================================================
339    // 6. Comparison table with border styles
340    // =========================================================================
341    doc.add_paragraph("6. Comparison Table with Custom Borders")
342        .style("Heading2");
343
344    {
345        let mut tbl = doc.add_table(5, 3);
346        tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347        tbl = tbl.width_pct(100.0);
348
349        // Header
350        tbl.cell(0, 0).unwrap().set_text("Feature");
351        tbl.cell(0, 0).unwrap().shading("2E75B6");
352        tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353        tbl.cell(0, 1).unwrap().shading("2E75B6");
354        tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355        tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357        tbl.cell(1, 0).unwrap().set_text("Users");
358        tbl.cell(1, 1).unwrap().set_text("Up to 10");
359        tbl.cell(1, 2).unwrap().set_text("Unlimited");
360        tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362        tbl.cell(2, 0).unwrap().set_text("Storage");
363        tbl.cell(2, 1).unwrap().set_text("50 GB");
364        tbl.cell(2, 2).unwrap().set_text("5 TB");
365        tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367        tbl.cell(3, 0).unwrap().set_text("Support");
368        tbl.cell(3, 1).unwrap().set_text("Email only");
369        tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370        tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372        tbl.cell(4, 0).unwrap().set_text("Price");
373        tbl.cell(4, 0).unwrap().shading("F2F2F2");
374        tbl.cell(4, 1).unwrap().set_text("$29/month");
375        tbl.cell(4, 1).unwrap().shading("F2F2F2");
376        tbl.cell(4, 2).unwrap().set_text("$199/month");
377        tbl.cell(4, 2).unwrap().shading("C6EFCE");
378    }
379
380    doc.add_paragraph("");
381
382    // =========================================================================
383    // 7. Wide table with fixed layout and row height
384    // =========================================================================
385    doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386        .style("Heading2");
387
388    {
389        let mut tbl = doc.add_table(4, 5);
390        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391        tbl = tbl.width(Length::inches(7.0));
392        tbl = tbl.layout_fixed();
393
394        // Set column widths
395        for col in 0..5 {
396            tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397        }
398
399        // Header with exact height
400        tbl.row(0).unwrap().height_exact(Length::twips(480));
401        tbl.row(0).unwrap().header();
402        tbl.row(0).unwrap().cant_split();
403
404        let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405        for (col, h) in headers.iter().enumerate() {
406            tbl.cell(0, col).unwrap().set_text(h);
407            tbl.cell(0, col).unwrap().shading("404040");
408            tbl.cell(0, col)
409                .unwrap()
410                .vertical_alignment(VerticalAlignment::Center);
411        }
412
413        // Schedule rows with minimum height
414        tbl.row(1).unwrap().height(Length::twips(600));
415        tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416        tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417        tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418        tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419        tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421        tbl.row(2).unwrap().height(Length::twips(600));
422        tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423        tbl.cell(2, 0).unwrap().shading("D6E4F0");
424        tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425        tbl.cell(2, 1).unwrap().shading("FCE4D6");
426        tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427        tbl.cell(2, 2).unwrap().shading("D6E4F0");
428        tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429        tbl.cell(2, 3).unwrap().shading("E2EFDA");
430        tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431        tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433        tbl.row(3).unwrap().height(Length::twips(600));
434        tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435        tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436        tbl.cell(3, 1).unwrap().shading("D6E4F0");
437        tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438        tbl.cell(3, 2).unwrap().shading("FCE4D6");
439        tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440        tbl.cell(3, 3).unwrap().shading("D6E4F0");
441        tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442        tbl.cell(3, 4).unwrap().shading("E2EFDA");
443    }
444
445    doc.set_title("Styled Tables Showcase");
446    doc.set_author("rdocx");
447
448    doc.save(path).unwrap();
449}
More examples
Hide additional examples
examples/generate_samples.rs (line 419)
34fn generate_feature_showcase(path: &Path) {
35    let mut doc = Document::new();
36
37    // =========================================================================
38    // PAGE SETUP & METADATA
39    // =========================================================================
40    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41    doc.set_margins(
42        Length::inches(1.0), // top
43        Length::inches(1.0), // right
44        Length::inches(1.0), // bottom
45        Length::inches(1.0), // left
46    );
47    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48    doc.set_gutter(Length::twips(0));
49
50    doc.set_title("rdocx Feature Showcase");
51    doc.set_author("rdocx Sample Generator");
52    doc.set_subject("Comprehensive feature demonstration");
53    doc.set_keywords("rdocx, docx, rust, sample");
54
55    // Header & Footer
56    doc.set_header("rdocx Feature Showcase");
57    doc.set_footer("Generated by rdocx — Page");
58
59    // Different first page header
60    doc.set_different_first_page(true);
61    doc.set_first_page_header("rdocx");
62    doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64    // =========================================================================
65    // PAGE 1: COVER PAGE — background image, run formatting
66    // =========================================================================
67    let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68    doc.add_background_image(&bg_cover, "cover_bg.png");
69
70    doc.add_paragraph(""); // spacer
71    doc.add_paragraph(""); // spacer
72    doc.add_paragraph(""); // spacer
73
74    {
75        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77    }
78    {
79        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80        p.add_run("Feature Showcase")
81            .size(28.0)
82            .color("FFFFFF")
83            .italic(true);
84    }
85
86    doc.add_paragraph(""); // spacer
87
88    {
89        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90        p.add_run("A comprehensive demonstration of every feature")
91            .size(14.0)
92            .color("CCDDFF");
93    }
94    {
95        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96        p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97            .size(14.0)
98            .color("CCDDFF");
99    }
100
101    // =========================================================================
102    // PAGE 2: TEXT FORMATTING
103    // =========================================================================
104    doc.add_paragraph("").page_break_before(true);
105
106    doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108    doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109    doc.add_paragraph("");
110
111    // --- Paragraph alignment ---
112    doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114    doc.add_paragraph("This paragraph is left-aligned (the default).")
115        .alignment(Alignment::Left);
116    doc.add_paragraph("This paragraph is center-aligned.")
117        .alignment(Alignment::Center);
118    doc.add_paragraph("This paragraph is right-aligned.")
119        .alignment(Alignment::Right);
120    doc.add_paragraph(
121        "This paragraph is justified. To demonstrate justified text properly, it needs \
122         to be long enough to span multiple lines so the word spacing adjustment is visible \
123         across the full width of the text area on the page.",
124    )
125    .alignment(Alignment::Justify);
126
127    doc.add_paragraph("");
128
129    // --- Run formatting ---
130    doc.add_paragraph("Run Formatting").style("Heading2");
131
132    {
133        let mut p = doc.add_paragraph("");
134        p.add_run("Bold text").bold(true);
135        p.add_run(" | ");
136        p.add_run("Italic text").italic(true);
137        p.add_run(" | ");
138        p.add_run("Bold + Italic").bold(true).italic(true);
139    }
140    {
141        let mut p = doc.add_paragraph("");
142        p.add_run("Single underline").underline(true);
143        p.add_run(" | ");
144        p.add_run("Strikethrough").strike(true);
145        p.add_run(" | ");
146        p.add_run("Double strikethrough").double_strike(true);
147    }
148    {
149        let mut p = doc.add_paragraph("");
150        p.add_run("Red text").color("FF0000");
151        p.add_run(" | ");
152        p.add_run("Blue text").color("0000FF");
153        p.add_run(" | ");
154        p.add_run("Green text").color("00AA00");
155        p.add_run(" | ");
156        p.add_run("Highlighted").highlight("FFFF00");
157    }
158    {
159        let mut p = doc.add_paragraph("");
160        p.add_run("8pt small").size(8.0);
161        p.add_run(" | ");
162        p.add_run("11pt normal").size(11.0);
163        p.add_run(" | ");
164        p.add_run("16pt large").size(16.0);
165        p.add_run(" | ");
166        p.add_run("24pt extra-large").size(24.0);
167    }
168    {
169        let mut p = doc.add_paragraph("");
170        p.add_run("Arial font").font("Arial");
171        p.add_run(" | ");
172        p.add_run("Times New Roman font").font("Times New Roman");
173        p.add_run(" | ");
174        p.add_run("Courier New font").font("Courier New");
175    }
176    {
177        let mut p = doc.add_paragraph("");
178        p.add_run("Normal");
179        p.add_run(" H").size(11.0);
180        p.add_run("2").subscript();
181        p.add_run("O (subscript)").size(11.0);
182        p.add_run(" | E = mc").size(11.0);
183        p.add_run("2").superscript();
184        p.add_run(" (superscript)").size(11.0);
185    }
186    {
187        let mut p = doc.add_paragraph("");
188        p.add_run("ALL CAPS").all_caps(true);
189        p.add_run(" | ");
190        p.add_run("Small Caps").small_caps(true);
191        p.add_run(" | ");
192        p.add_run("Expanded spacing")
193            .character_spacing(Length::twips(40));
194    }
195
196    doc.add_paragraph("");
197
198    // --- Paragraph formatting ---
199    doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201    doc.add_paragraph("Paragraph with shading (light green background)")
202        .shading("E2EFDA");
203
204    doc.add_paragraph("Paragraph with bottom border")
205        .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207    doc.add_paragraph("Paragraph with all borders")
208        .border_all(BorderStyle::Single, 4, "FF0000");
209
210    doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211        .indent_left(Length::inches(1.0))
212        .hanging_indent(Length::inches(0.5));
213
214    doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215        .first_line_indent(Length::inches(0.5));
216
217    doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218        .space_before(Length::pt(24.0))
219        .space_after(Length::pt(12.0));
220
221    doc.add_paragraph(
222        "Paragraph with double line spacing. This text should have extra vertical \
223         space between lines to demonstrate the line_spacing_multiple setting.",
224    )
225    .line_spacing_multiple(2.0);
226
227    doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228        .keep_with_next(true);
229    doc.add_paragraph("(This stays with the paragraph above.)");
230
231    // =========================================================================
232    // PAGE 3: LISTS & TAB STOPS
233    // =========================================================================
234    doc.add_paragraph("").page_break_before(true);
235
236    doc.add_paragraph("2. Lists").style("Heading1");
237
238    doc.add_paragraph("Bullet List").style("Heading2");
239
240    doc.add_bullet_list_item("First bullet item", 0);
241    doc.add_bullet_list_item("Second bullet item", 0);
242    doc.add_bullet_list_item("Nested level 1", 1);
243    doc.add_bullet_list_item("Nested level 2", 2);
244    doc.add_bullet_list_item("Back to level 1", 1);
245    doc.add_bullet_list_item("Third bullet item", 0);
246
247    doc.add_paragraph("");
248
249    doc.add_paragraph("Numbered List").style("Heading2");
250
251    doc.add_numbered_list_item("First numbered item", 0);
252    doc.add_numbered_list_item("Second numbered item", 0);
253    doc.add_numbered_list_item("Sub-item A", 1);
254    doc.add_numbered_list_item("Sub-item B", 1);
255    doc.add_numbered_list_item("Third numbered item", 0);
256
257    doc.add_paragraph("");
258
259    // --- Tab stops ---
260    doc.add_paragraph("Tab Stops").style("Heading2");
261
262    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268    doc.add_paragraph("Item\t........\tPrice")
269        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273    doc.add_paragraph("Widget A\t........\t$19.99")
274        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278    doc.add_paragraph("Gadget B\t________\t$249.50")
279        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280        .add_tab_stop_with_leader(
281            TabAlignment::Right,
282            Length::inches(4.0),
283            TabLeader::Underscore,
284        )
285        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287    // =========================================================================
288    // PAGE 4: TABLES
289    // =========================================================================
290    doc.add_paragraph("").page_break_before(true);
291
292    doc.add_paragraph("3. Tables").style("Heading1");
293
294    // --- Basic table with borders ---
295    doc.add_paragraph("Basic Table with Borders")
296        .style("Heading2");
297
298    {
299        let mut tbl = doc.add_table(4, 3);
300        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302        // Header row
303        for col in 0..3 {
304            tbl.cell(0, col).unwrap().shading("2E75B6");
305        }
306        tbl.cell(0, 0).unwrap().set_text("Name");
307        tbl.cell(0, 1).unwrap().set_text("Role");
308        tbl.cell(0, 2).unwrap().set_text("Location");
309
310        tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311        tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312        tbl.cell(1, 2).unwrap().set_text("New York");
313
314        tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315        tbl.cell(2, 1).unwrap().set_text("Product Manager");
316        tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318        tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319        tbl.cell(3, 1).unwrap().set_text("Designer");
320        tbl.cell(3, 2).unwrap().set_text("London");
321    }
322
323    doc.add_paragraph("");
324
325    // --- Table with cell merging ---
326    doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327        .style("Heading2");
328
329    {
330        let mut tbl = doc.add_table(4, 4);
331        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332        tbl = tbl.width_pct(100.0);
333
334        // Header spanning all columns
335        tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336        tbl.cell(0, 0).unwrap().shading("1F4E79");
337        tbl.cell(0, 0).unwrap().grid_span(4);
338
339        // Sub-header
340        tbl.cell(1, 0).unwrap().set_text("Region");
341        tbl.cell(1, 0).unwrap().shading("D6E4F0");
342        tbl.cell(1, 1).unwrap().set_text("Q1");
343        tbl.cell(1, 1).unwrap().shading("D6E4F0");
344        tbl.cell(1, 2).unwrap().set_text("Q2");
345        tbl.cell(1, 2).unwrap().shading("D6E4F0");
346        tbl.cell(1, 3).unwrap().set_text("Total");
347        tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349        // Data
350        tbl.cell(2, 0).unwrap().set_text("North America");
351        tbl.cell(2, 1).unwrap().set_text("$2.4M");
352        tbl.cell(2, 2).unwrap().set_text("$2.7M");
353        tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355        tbl.cell(3, 0).unwrap().set_text("Europe");
356        tbl.cell(3, 1).unwrap().set_text("$1.8M");
357        tbl.cell(3, 2).unwrap().set_text("$2.0M");
358        tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360        // Vertical alignment on data cells
361        tbl.cell(2, 3)
362            .unwrap()
363            .vertical_alignment(VerticalAlignment::Center);
364        tbl.cell(3, 3)
365            .unwrap()
366            .vertical_alignment(VerticalAlignment::Bottom);
367    }
368
369    doc.add_paragraph("");
370
371    // --- Table with vertical merge ---
372    doc.add_paragraph("Table with Vertical Merge")
373        .style("Heading2");
374
375    {
376        let mut tbl = doc.add_table(4, 3);
377        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379        tbl.cell(0, 0).unwrap().set_text("Category");
380        tbl.cell(0, 0).unwrap().shading("E2EFDA");
381        tbl.cell(0, 1).unwrap().set_text("Item");
382        tbl.cell(0, 1).unwrap().shading("E2EFDA");
383        tbl.cell(0, 2).unwrap().set_text("Price");
384        tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386        // "Hardware" spans rows 1-2
387        tbl.cell(1, 0).unwrap().set_text("Hardware");
388        tbl.cell(1, 0).unwrap().v_merge_restart();
389        tbl.cell(1, 1).unwrap().set_text("Laptop");
390        tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392        tbl.cell(2, 0).unwrap().v_merge_continue();
393        tbl.cell(2, 1).unwrap().set_text("Monitor");
394        tbl.cell(2, 2).unwrap().set_text("$450");
395
396        // "Software" on row 3
397        tbl.cell(3, 0).unwrap().set_text("Software");
398        tbl.cell(3, 1).unwrap().set_text("IDE License");
399        tbl.cell(3, 2).unwrap().set_text("$200/yr");
400    }
401
402    doc.add_paragraph("");
403
404    // --- Nested table ---
405    doc.add_paragraph("Nested Table").style("Heading2");
406
407    {
408        let mut tbl = doc.add_table(2, 2);
409        tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411        tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412        tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413        tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415        // Nested table inside cell (1,1)
416        {
417            let mut cell = tbl.cell(1, 1).unwrap();
418            cell.set_text("Contains nested table:");
419            let mut nested = cell.add_table(2, 2);
420            nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421            nested.cell(0, 0).unwrap().set_text("Inner A");
422            nested.cell(0, 1).unwrap().set_text("Inner B");
423            nested.cell(1, 0).unwrap().set_text("Inner C");
424            nested.cell(1, 1).unwrap().set_text("Inner D");
425        }
426    }
427
428    // =========================================================================
429    // PAGE 5: IMAGES
430    // =========================================================================
431    doc.add_paragraph("").page_break_before(true);
432
433    doc.add_paragraph("4. Images").style("Heading1");
434
435    doc.add_paragraph("Inline Image").style("Heading2");
436
437    doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438    let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439    doc.add_picture(
440        &inline_img,
441        "inline_chart.png",
442        Length::inches(3.0),
443        Length::inches(0.75),
444    );
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("Header Image").style("Heading2");
449
450    // Replace the text-only header with an image header
451    let header_img = create_sample_png(400, 40, [40, 40, 40]);
452    doc.set_header_image(
453        &header_img,
454        "header_logo.png",
455        Length::inches(2.0),
456        Length::inches(0.2),
457    );
458
459    doc.add_paragraph(
460        "The document header has been replaced with an inline image. \
461         Check the header area at the top of this page.",
462    );
463
464    doc.add_paragraph("");
465    doc.add_paragraph(
466        "Note: The cover page uses a full-page background image behind the text, \
467         demonstrated on page 1 via add_background_image().",
468    );
469
470    // =========================================================================
471    // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472    // =========================================================================
473    doc.add_paragraph("").page_break_before(true);
474
475    doc.add_paragraph("5. Content Manipulation")
476        .style("Heading1");
477
478    // --- Placeholder replacement ---
479    doc.add_paragraph("Placeholder Replacement")
480        .style("Heading2");
481
482    doc.add_paragraph(
483        "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484    );
485
486    {
487        let mut p = doc.add_paragraph("");
488        p.add_run("Customer: ").bold(true);
489        p.add_run("{{customer}}");
490    }
491    {
492        let mut p = doc.add_paragraph("");
493        p.add_run("Date: ").bold(true);
494        p.add_run("{{date}}");
495    }
496    doc.add_paragraph("Reference: {{ref_number}}");
497
498    // Table with placeholders
499    {
500        let mut tbl = doc.add_table(3, 2);
501        tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502        tbl.cell(0, 0).unwrap().set_text("Field");
503        tbl.cell(0, 0).unwrap().shading("D6E4F0");
504        tbl.cell(0, 1).unwrap().set_text("Value");
505        tbl.cell(0, 1).unwrap().shading("D6E4F0");
506        tbl.cell(1, 0).unwrap().set_text("Project");
507        tbl.cell(1, 1).unwrap().set_text("{{project}}");
508        tbl.cell(2, 0).unwrap().set_text("Status");
509        tbl.cell(2, 1).unwrap().set_text("{{status}}");
510    }
511
512    // Perform replacements
513    let mut replacements = HashMap::new();
514    replacements.insert("{{customer}}", "Acme Corporation");
515    replacements.insert("{{date}}", "February 22, 2026");
516    replacements.insert("{{ref_number}}", "REF-2026-001");
517    replacements.insert("{{project}}", "Infrastructure Upgrade");
518    replacements.insert("{{status}}", "In Progress");
519    let replace_count = doc.replace_all(&replacements);
520
521    doc.add_paragraph("");
522    doc.add_paragraph(&format!(
523        "(Replaced {} placeholders above — in body text and table cells)",
524        replace_count
525    ));
526
527    doc.add_paragraph("");
528
529    // --- Content insertion ---
530    doc.add_paragraph("Content Insertion").style("Heading2");
531
532    doc.add_paragraph("Section A: First section of content.");
533    doc.add_paragraph("Section C: Third section of content.");
534
535    // Insert "Section B" between A and C
536    if let Some(idx) = doc.find_content_index("Section C") {
537        doc.insert_paragraph(
538            idx,
539            "Section B: Inserted between A and C using find_content_index().",
540        );
541    }
542
543    doc.add_paragraph("");
544    doc.add_paragraph(
545        "The paragraph above ('Section B') was inserted at a specific position \
546         using find_content_index() + insert_paragraph().",
547    );
548
549    // =========================================================================
550    // PAGE 7: LANDSCAPE — section break, wide table
551    // =========================================================================
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554    doc.add_paragraph("6. Mixed Page Orientation")
555        .style("Heading1");
556
557    doc.add_paragraph(
558        "This page is in LANDSCAPE orientation. It was created using a section break \
559         followed by section_landscape(). This is useful for wide tables or charts.",
560    );
561
562    doc.add_paragraph("");
563
564    // Wide table for landscape
565    {
566        let mut tbl = doc.add_table(4, 7);
567        tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570        for (col, h) in headers.iter().enumerate() {
571            tbl.cell(0, col).unwrap().set_text(h);
572            tbl.cell(0, col).unwrap().shading("2E75B6");
573        }
574
575        let data = [
576            [
577                "North America",
578                "$1.2M",
579                "$1.3M",
580                "$1.4M",
581                "$1.5M",
582                "$1.6M",
583                "$7.0M",
584            ],
585            [
586                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587            ],
588            [
589                "Asia Pacific",
590                "$0.5M",
591                "$0.6M",
592                "$0.7M",
593                "$0.7M",
594                "$0.8M",
595                "$3.3M",
596            ],
597        ];
598        for (row_idx, row_data) in data.iter().enumerate() {
599            for (col, val) in row_data.iter().enumerate() {
600                tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601            }
602        }
603    }
604
605    // End landscape, return to portrait
606    doc.add_paragraph("")
607        .section_break(SectionBreak::NextPage)
608        .section_landscape();
609
610    // =========================================================================
611    // PAGE 8: BACK TO PORTRAIT — styles, final notes
612    // =========================================================================
613    doc.add_paragraph("7. Custom Styles & Summary")
614        .style("Heading1");
615
616    doc.add_paragraph(
617        "This final page is back in portrait orientation after a section break. \
618         The document has demonstrated:",
619    );
620
621    doc.add_bullet_list_item(
622        "Page setup: size, margins, header/footer distance, gutter",
623        0,
624    );
625    doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626    doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627    doc.add_bullet_list_item("Background images: full-page behind text", 0);
628    doc.add_bullet_list_item(
629        "Text formatting: bold, italic, underline, strike, color, size, font",
630        0,
631    );
632    doc.add_bullet_list_item(
633        "Advanced run formatting: superscript, subscript, caps, spacing",
634        0,
635    );
636    doc.add_bullet_list_item(
637        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638        0,
639    );
640    doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641    doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642    doc.add_bullet_list_item(
643        "Tables: borders, shading, column spans, row spans, nesting",
644        0,
645    );
646    doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647    doc.add_bullet_list_item("Inline images", 0);
648    doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649    doc.add_bullet_list_item("Content insertion at specific positions", 0);
650    doc.add_bullet_list_item(
651        "Section breaks with mixed portrait/landscape orientation",
652        0,
653    );
654
655    doc.add_paragraph("");
656    doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657        .alignment(Alignment::Center)
658        .shading("E2EFDA")
659        .border_all(BorderStyle::Single, 2, "00AA00");
660
661    doc.save(path).unwrap();
662}
examples/generate_all_samples.rs (line 437)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87    let mut doc = Document::new();
88
89    // ── Page Setup & Metadata ──
90    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91    doc.set_margins(
92        Length::inches(1.0),
93        Length::inches(1.0),
94        Length::inches(1.0),
95        Length::inches(1.0),
96    );
97    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98    doc.set_gutter(Length::twips(0));
99    doc.set_title("rdocx Feature Showcase");
100    doc.set_author("rdocx Sample Generator");
101    doc.set_subject("Comprehensive feature demonstration");
102    doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104    // Headers & Footers with different first page
105    doc.set_different_first_page(true);
106    doc.set_first_page_header("rdocx");
107    doc.set_first_page_footer("Feature Showcase — Cover Page");
108    doc.set_header("rdocx Feature Showcase");
109    doc.set_footer("Generated by rdocx");
110
111    // ── COVER PAGE ──
112    let bg = create_sample_png(612, 792, [20, 45, 90]);
113    doc.add_background_image(&bg, "cover_bg.png");
114
115    for _ in 0..3 {
116        doc.add_paragraph("");
117    }
118    {
119        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121    }
122    {
123        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124        p.add_run("Complete Feature Showcase")
125            .size(28.0)
126            .color("FFFFFF")
127            .italic(true);
128    }
129    doc.add_paragraph("");
130    {
131        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132        p.add_run("Every feature of the rdocx Rust library")
133            .size(13.0)
134            .color("B0C4DE");
135    }
136    {
137        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138        p.add_run("demonstrated in a single document.")
139            .size(13.0)
140            .color("B0C4DE");
141    }
142
143    // ── TABLE OF CONTENTS ──
144    doc.add_paragraph("").page_break_before(true);
145    doc.insert_toc(doc.content_count(), 3);
146
147    // ── SECTION 1: TEXT FORMATTING ──
148    doc.add_paragraph("").page_break_before(true);
149    doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151    // Paragraph Alignment
152    doc.add_paragraph("1.1 Paragraph Alignment")
153        .style("Heading2");
154    doc.add_paragraph("Left-aligned paragraph (default).")
155        .alignment(Alignment::Left);
156    doc.add_paragraph("Center-aligned paragraph.")
157        .alignment(Alignment::Center);
158    doc.add_paragraph("Right-aligned paragraph.")
159        .alignment(Alignment::Right);
160    doc.add_paragraph(
161        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162         distributes extra space across word gaps so lines fill the full width of the text area.",
163    )
164    .alignment(Alignment::Justify);
165
166    doc.add_paragraph("");
167
168    // Run Formatting
169    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170    {
171        let mut p = doc.add_paragraph("");
172        p.add_run("Bold").bold(true);
173        p.add_run(" | ");
174        p.add_run("Italic").italic(true);
175        p.add_run(" | ");
176        p.add_run("Bold Italic").bold(true).italic(true);
177        p.add_run(" | ");
178        p.add_run("Underline").underline(true);
179        p.add_run(" | ");
180        p.add_run("Strikethrough").strike(true);
181        p.add_run(" | ");
182        p.add_run("Double Strike").double_strike(true);
183    }
184    {
185        let mut p = doc.add_paragraph("");
186        p.add_run("Red").color("FF0000");
187        p.add_run(" | ");
188        p.add_run("Blue").color("0000FF");
189        p.add_run(" | ");
190        p.add_run("Green").color("00AA00");
191        p.add_run(" | ");
192        p.add_run("Highlighted").highlight("FFFF00");
193    }
194    {
195        let mut p = doc.add_paragraph("");
196        p.add_run("8pt").size(8.0);
197        p.add_run(" | ");
198        p.add_run("11pt").size(11.0);
199        p.add_run(" | ");
200        p.add_run("16pt").size(16.0);
201        p.add_run(" | ");
202        p.add_run("24pt").size(24.0);
203    }
204    {
205        let mut p = doc.add_paragraph("");
206        p.add_run("Arial").font("Arial");
207        p.add_run(" | ");
208        p.add_run("Times New Roman").font("Times New Roman");
209        p.add_run(" | ");
210        p.add_run("Courier New").font("Courier New");
211    }
212    {
213        let mut p = doc.add_paragraph("");
214        p.add_run("H");
215        p.add_run("2").subscript();
216        p.add_run("O (subscript) | E=mc");
217        p.add_run("2").superscript();
218        p.add_run(" (superscript)");
219    }
220    {
221        let mut p = doc.add_paragraph("");
222        p.add_run("ALL CAPS").all_caps(true);
223        p.add_run(" | ");
224        p.add_run("Small Caps").small_caps(true);
225        p.add_run(" | ");
226        p.add_run("Expanded").character_spacing(Length::twips(40));
227        p.add_run(" | ");
228        p.add_run("Hidden text (not visible)").hidden(true);
229    }
230
231    doc.add_paragraph("");
232
233    // Underline Styles
234    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235    {
236        let mut p = doc.add_paragraph("");
237        p.add_run("Single ")
238            .underline_style(rdocx::UnderlineStyle::Single);
239        p.add_run("Double ")
240            .underline_style(rdocx::UnderlineStyle::Double);
241        p.add_run("Thick ")
242            .underline_style(rdocx::UnderlineStyle::Thick);
243        p.add_run("Dotted ")
244            .underline_style(rdocx::UnderlineStyle::Dotted);
245        p.add_run("Dash ")
246            .underline_style(rdocx::UnderlineStyle::Dash);
247        p.add_run("Wave ")
248            .underline_style(rdocx::UnderlineStyle::Wave);
249    }
250
251    // ── SECTION 2: PARAGRAPH FORMATTING ──
252    doc.add_paragraph("").page_break_before(true);
253    doc.add_paragraph("2. Paragraph Formatting")
254        .style("Heading1");
255
256    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257    doc.add_paragraph("Paragraph with light green shading")
258        .shading("E2EFDA");
259    doc.add_paragraph("Paragraph with bottom border")
260        .border_bottom(BorderStyle::Single, 6, "2E75B6");
261    doc.add_paragraph("Paragraph with all borders (red)")
262        .border_all(BorderStyle::Single, 4, "FF0000");
263
264    doc.add_paragraph("2.2 Indentation").style("Heading2");
265    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266        .indent_left(Length::inches(1.0))
267        .hanging_indent(Length::inches(0.5));
268    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269        .first_line_indent(Length::inches(0.5));
270    doc.add_paragraph("Both left and right indent (0.75 inches each)")
271        .indent_left(Length::inches(0.75))
272        .indent_right(Length::inches(0.75));
273
274    doc.add_paragraph("2.3 Spacing & Line Height")
275        .style("Heading2");
276    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277        .space_before(Length::pt(24.0))
278        .space_after(Length::pt(12.0));
279    doc.add_paragraph(
280        "Double line spacing paragraph. This text should have extra vertical space between \
281         lines to demonstrate line_spacing_multiple(2.0).",
282    )
283    .line_spacing_multiple(2.0);
284    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285        .line_spacing(20.0);
286
287    doc.add_paragraph("2.4 Pagination Controls")
288        .style("Heading2");
289    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290        .keep_with_next(true);
291    doc.add_paragraph("(This paragraph was kept with the one above.)");
292    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293        .keep_together(true);
294    doc.add_paragraph("widow_control — prevents widow/orphan lines")
295        .widow_control(true);
296
297    // ── SECTION 3: LISTS ──
298    doc.add_paragraph("").page_break_before(true);
299    doc.add_paragraph("3. Lists").style("Heading1");
300
301    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302    doc.add_bullet_list_item("First item", 0);
303    doc.add_bullet_list_item("Second item", 0);
304    doc.add_bullet_list_item("Nested level 1", 1);
305    doc.add_bullet_list_item("Nested level 2", 2);
306    doc.add_bullet_list_item("Back to level 1", 1);
307    doc.add_bullet_list_item("Third item", 0);
308
309    doc.add_paragraph("");
310
311    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312    doc.add_numbered_list_item("First numbered item", 0);
313    doc.add_numbered_list_item("Second numbered item", 0);
314    doc.add_numbered_list_item("Sub-item A", 1);
315    doc.add_numbered_list_item("Sub-item B", 1);
316    doc.add_numbered_list_item("Third numbered item", 0);
317
318    // ── SECTION 4: TAB STOPS ──
319    doc.add_paragraph("");
320    doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330    doc.add_paragraph("Item\t\tPrice")
331        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334    doc.add_paragraph("Widget\t\t$19.99")
335        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338    doc.add_paragraph("Gadget\t\t$249.50")
339        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340        .add_tab_stop_with_leader(
341            TabAlignment::Right,
342            Length::inches(4.0),
343            TabLeader::Underscore,
344        )
345        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347    // ── SECTION 5: TABLES ──
348    doc.add_paragraph("").page_break_before(true);
349    doc.add_paragraph("5. Tables").style("Heading1");
350
351    doc.add_paragraph("5.1 Basic Table").style("Heading2");
352    {
353        let mut tbl = doc
354            .add_table(4, 3)
355            .borders(BorderStyle::Single, 4, "000000");
356        for col in 0..3 {
357            tbl.cell(0, col).unwrap().shading("2E75B6");
358        }
359        tbl.cell(0, 0).unwrap().set_text("Name");
360        tbl.cell(0, 1).unwrap().set_text("Role");
361        tbl.cell(0, 2).unwrap().set_text("Location");
362        tbl.cell(1, 0).unwrap().set_text("Walter White");
363        tbl.cell(1, 1).unwrap().set_text("CEO");
364        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366        tbl.cell(2, 1).unwrap().set_text("CTO");
367        tbl.cell(2, 2).unwrap().set_text("Remote");
368        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369        tbl.cell(3, 1).unwrap().set_text("Security");
370        tbl.cell(3, 2).unwrap().set_text("Washington");
371    }
372
373    doc.add_paragraph("");
374
375    doc.add_paragraph("5.2 Column Span & Cell Shading")
376        .style("Heading2");
377    {
378        let mut tbl = doc
379            .add_table(3, 4)
380            .borders(BorderStyle::Single, 4, "000000")
381            .width_pct(100.0);
382        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384        tbl.cell(1, 0).unwrap().set_text("Region");
385        tbl.cell(1, 0).unwrap().shading("D6E4F0");
386        tbl.cell(1, 1).unwrap().set_text("Q1");
387        tbl.cell(1, 1).unwrap().shading("D6E4F0");
388        tbl.cell(1, 2).unwrap().set_text("Q2");
389        tbl.cell(1, 2).unwrap().shading("D6E4F0");
390        tbl.cell(1, 3).unwrap().set_text("Total");
391        tbl.cell(1, 3).unwrap().shading("D6E4F0");
392        tbl.cell(2, 0).unwrap().set_text("Americas");
393        tbl.cell(2, 1).unwrap().set_text("$2.4M");
394        tbl.cell(2, 2).unwrap().set_text("$2.7M");
395        tbl.cell(2, 3).unwrap().set_text("$5.1M");
396    }
397
398    doc.add_paragraph("");
399
400    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401    {
402        let mut tbl = doc
403            .add_table(4, 3)
404            .borders(BorderStyle::Single, 4, "000000");
405        tbl.cell(0, 0).unwrap().set_text("Category");
406        tbl.cell(0, 0).unwrap().shading("E2EFDA");
407        tbl.cell(0, 1).unwrap().set_text("Item");
408        tbl.cell(0, 1).unwrap().shading("E2EFDA");
409        tbl.cell(0, 2).unwrap().set_text("Price");
410        tbl.cell(0, 2).unwrap().shading("E2EFDA");
411        tbl.cell(1, 0).unwrap().set_text("Hardware");
412        tbl.cell(1, 0).unwrap().v_merge_restart();
413        tbl.cell(1, 1).unwrap().set_text("Laptop");
414        tbl.cell(1, 2).unwrap().set_text("$1,200");
415        tbl.cell(2, 0).unwrap().v_merge_continue();
416        tbl.cell(2, 1).unwrap().set_text("Monitor");
417        tbl.cell(2, 2).unwrap().set_text("$450");
418        tbl.cell(3, 0).unwrap().set_text("Software");
419        tbl.cell(3, 1).unwrap().set_text("IDE License");
420        tbl.cell(3, 2).unwrap().set_text("$200/yr");
421    }
422
423    doc.add_paragraph("");
424
425    doc.add_paragraph("5.4 Nested Table").style("Heading2");
426    {
427        let mut tbl = doc
428            .add_table(2, 2)
429            .borders(BorderStyle::Single, 6, "2E75B6");
430        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433        {
434            let mut cell = tbl.cell(1, 1).unwrap();
435            cell.set_text("Nested table below:");
436            let mut nested = cell
437                .add_table(2, 2)
438                .borders(BorderStyle::Single, 2, "FF6600");
439            nested.cell(0, 0).unwrap().set_text("A");
440            nested.cell(0, 1).unwrap().set_text("B");
441            nested.cell(1, 0).unwrap().set_text("C");
442            nested.cell(1, 1).unwrap().set_text("D");
443        }
444    }
445
446    doc.add_paragraph("");
447
448    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449        .style("Heading2");
450    {
451        let mut tbl = doc
452            .add_table(2, 3)
453            .borders(BorderStyle::Single, 4, "666666");
454        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455        tbl.cell(0, 0).unwrap().set_text("Top");
456        tbl.cell(0, 0)
457            .unwrap()
458            .vertical_alignment(VerticalAlignment::Top)
459            .shading("FFF2CC");
460        tbl.cell(0, 1).unwrap().set_text("Center");
461        tbl.cell(0, 1)
462            .unwrap()
463            .vertical_alignment(VerticalAlignment::Center)
464            .shading("D9E2F3");
465        tbl.cell(0, 2).unwrap().set_text("Bottom");
466        tbl.cell(0, 2)
467            .unwrap()
468            .vertical_alignment(VerticalAlignment::Bottom)
469            .shading("E2EFDA");
470        tbl.row(1).unwrap().cant_split();
471        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472        tbl.cell(1, 0).unwrap().no_wrap();
473        tbl.cell(1, 1).unwrap().set_text("Fixed width");
474        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475        tbl.cell(1, 2).unwrap().set_text("Normal");
476    }
477
478    // ── SECTION 6: IMAGES ──
479    doc.add_paragraph("").page_break_before(true);
480    doc.add_paragraph("6. Images").style("Heading1");
481
482    doc.add_paragraph("6.1 Inline Image").style("Heading2");
483    doc.add_paragraph("A blue gradient image below:");
484    let img = create_sample_png(200, 50, [0, 80, 200]);
485    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487    doc.add_paragraph("");
488    doc.add_paragraph("6.2 Header Image").style("Heading2");
489    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490    doc.set_header_image(
491        &hdr_img,
492        "header_logo.png",
493        Length::inches(2.0),
494        Length::inches(0.2),
495    );
496    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498    doc.add_paragraph("");
499    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501    // ── SECTION 7: CONTENT MANIPULATION ──
502    doc.add_paragraph("").page_break_before(true);
503    doc.add_paragraph("7. Content Manipulation")
504        .style("Heading1");
505
506    doc.add_paragraph("7.1 Placeholder Replacement")
507        .style("Heading2");
508    doc.add_paragraph("Customer: {{customer}}");
509    doc.add_paragraph("Date: {{date}}");
510    doc.add_paragraph("Reference: {{ref_number}}");
511    {
512        let mut tbl = doc
513            .add_table(2, 2)
514            .borders(BorderStyle::Single, 4, "000000");
515        tbl.cell(0, 0).unwrap().set_text("Project");
516        tbl.cell(0, 0).unwrap().shading("D6E4F0");
517        tbl.cell(0, 1).unwrap().set_text("{{project}}");
518        tbl.cell(1, 0).unwrap().set_text("Status");
519        tbl.cell(1, 0).unwrap().shading("D6E4F0");
520        tbl.cell(1, 1).unwrap().set_text("{{status}}");
521    }
522
523    let mut replacements = HashMap::new();
524    replacements.insert("{{customer}}", "Tensorbee Inc.");
525    replacements.insert("{{date}}", "February 22, 2026");
526    replacements.insert("{{ref_number}}", "TB-2026-001");
527    replacements.insert("{{project}}", "Infrastructure Upgrade");
528    replacements.insert("{{status}}", "In Progress");
529    let n = doc.replace_all(&replacements);
530    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532    doc.add_paragraph("");
533
534    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539    doc.add_paragraph("");
540
541    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542    doc.add_paragraph("Section A: First content.");
543    doc.add_paragraph("Section C: Third content.");
544    if let Some(idx) = doc.find_content_index("Section C") {
545        doc.insert_paragraph(
546            idx,
547            "Section B: Inserted between A and C via find_content_index().",
548        );
549    }
550
551    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552    doc.add_paragraph("").section_break(SectionBreak::NextPage);
553    doc.add_paragraph("8. Section Breaks & Orientation")
554        .style("Heading1");
555    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557    {
558        let mut tbl = doc
559            .add_table(3, 7)
560            .borders(BorderStyle::Single, 4, "2E75B6");
561        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562        for (c, h) in headers.iter().enumerate() {
563            tbl.cell(0, c).unwrap().set_text(h);
564            tbl.cell(0, c).unwrap().shading("2E75B6");
565        }
566        let data = [
567            [
568                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569            ],
570            [
571                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572            ],
573        ];
574        for (r, row) in data.iter().enumerate() {
575            for (c, v) in row.iter().enumerate() {
576                tbl.cell(r + 1, c).unwrap().set_text(v);
577            }
578        }
579    }
580
581    doc.add_paragraph("")
582        .section_break(SectionBreak::NextPage)
583        .section_landscape();
584
585    // ── SECTION 9: CUSTOM STYLES ──
586    doc.add_paragraph("9. Custom Styles").style("Heading1");
587    doc.add_style(
588        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589            .based_on("Normal")
590            .paragraph_properties({
591                let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592                ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593                    val: "clear".to_string(),
594                    color: None,
595                    fill: Some("FFF2CC".to_string()),
596                });
597                ppr
598            })
599            .run_properties({
600                let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601                rpr.bold = Some(true);
602                rpr.color = Some("C45911".to_string());
603                rpr
604            }),
605    );
606    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607        .style("CustomHighlight");
608
609    doc.add_paragraph("");
610
611    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612    doc.add_paragraph("10. Document Intelligence API")
613        .style("Heading1");
614
615    let wc = doc.word_count();
616    let hc = doc.headings().len();
617    let ic = doc.images().len();
618    let lc = doc.links().len();
619    doc.add_paragraph(&format!("Word count: {wc}"));
620    doc.add_paragraph(&format!("Heading count: {hc}"));
621    doc.add_paragraph(&format!("Image count: {ic}"));
622    doc.add_paragraph(&format!("Link count: {lc}"));
623
624    let outline = doc.document_outline();
625    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627    let issues = doc.audit_accessibility();
628    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629    for issue in &issues {
630        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631    }
632
633    // ── SECTION 11: DOCUMENT MERGING ──
634    doc.add_paragraph("");
635    doc.add_paragraph("11. Document Merging").style("Heading1");
636    {
637        let mut other = Document::new();
638        other.add_paragraph("This paragraph was merged from another document using append().");
639        doc.append(&other);
640    }
641
642    doc.add_paragraph("");
643
644    // ── SUMMARY ──
645    doc.add_paragraph("Summary of Demonstrated Features")
646        .style("Heading1");
647    let features = [
648        "Page setup: size, margins, header/footer distance, gutter",
649        "Document metadata: title, author, subject, keywords",
650        "Headers and footers: text, images, different first page",
651        "Background images (full-page behind text)",
652        "Table of Contents generation with bookmarks",
653        "Text formatting: bold, italic, underline styles, strike, color, size, font",
654        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656        "Pagination controls: keep-with-next, keep-together, widow control",
657        "Bullet and numbered lists with nesting",
658        "Tab stops with dot/underscore/hyphen leaders",
659        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660        "Row properties: header rows, exact height, cant-split",
661        "Cell properties: width, no-wrap, vertical alignment",
662        "Inline images and header images",
663        "Placeholder replacement (single and batch)",
664        "Regex-based find and replace",
665        "Content insertion at specific positions",
666        "Section breaks with mixed portrait/landscape",
667        "Custom paragraph and character styles",
668        "Document intelligence: word count, headings, outline, images, links",
669        "Accessibility audit",
670        "Document merging (append)",
671        "Export: DOCX, PDF, HTML, Markdown",
672    ];
673    for f in &features {
674        doc.add_bullet_list_item(f, 0);
675    }
676    doc.add_paragraph("");
677    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678        .alignment(Alignment::Center)
679        .shading("E2EFDA")
680        .border_all(BorderStyle::Single, 2, "00AA00");
681
682    doc
683}

Auto Trait Implementations§

§

impl<'a> Freeze for Cell<'a>

§

impl<'a> RefUnwindSafe for Cell<'a>

§

impl<'a> Send for Cell<'a>

§

impl<'a> Sync for Cell<'a>

§

impl<'a> Unpin for Cell<'a>

§

impl<'a> UnsafeUnpin for Cell<'a>

§

impl<'a> !UnwindSafe for Cell<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Finish for T

Source§

fn finish(self)

Does nothing but move self, equivalent to drop.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<U, T> ToOwnedObj<U> for T
where U: FromObjRef<T>,

Source§

fn to_owned_obj(&self, data: FontData<'_>) -> U

Convert this type into T, using the provided data to resolve any offsets.
Source§

impl<U, T> ToOwnedTable<U> for T
where U: FromTableRef<T>,

Source§

fn to_owned_table(&self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.