Skip to main content

Paragraph

Struct Paragraph 

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

A mutable reference to a paragraph in a document.

Implementations§

Source§

impl<'a> Paragraph<'a>

Source

pub fn text(&self) -> String

Get the combined text of all runs.

Source

pub fn add_footnote_ref(&mut self, id: i32)

Append a footnote reference run (<w:footnoteReference w:id="..."/>). The footnote content itself is added via Document::add_footnote.

Source

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

Add a run with the given text and return a mutable reference for chaining.

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

pub fn add_line_break(&mut self)

Add a line break in its own run.

Add a run wrapped in an external hyperlink relationship.

Obtain relationship_id from crate::Document::add_hyperlink_relationship. Returning the run allows the hyperlink text to receive the same direct formatting as any other run.

Source

pub fn run_count(&self) -> usize

Get the number of runs in this paragraph.

Source

pub fn run(&self, index: usize) -> Option<RunRef<'_>>

Get an immutable run by index.

Source

pub fn run_mut(&mut self, index: usize) -> Option<Run<'_>>

Get a mutable run by index.

Source

pub fn runs(&self) -> impl Iterator<Item = RunRef<'_>>

Get an iterator over immutable run references.

Source

pub fn alignment(self, align: Alignment) -> Self

Set the paragraph alignment.

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

pub fn set_alignment(&mut self, align: Alignment)

Set the paragraph alignment in place.

Source

pub fn set_alignment_value(&mut self, align: Option<Alignment>)

Set or clear direct paragraph alignment.

Source

pub fn style(self, style_id: &str) -> Self

Set the paragraph style by ID.

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

pub fn set_style(&mut self, style_id: &str)

Set the paragraph style by ID in place.

Source

pub fn numbering(self, num_id: u32, level: u32) -> Self

Attach this paragraph to a list definition as a list item.

num_id comes from crate::Document::add_list_definition (or the shared definitions behind add_bullet_list_item / add_numbered_list_item); level is the 0-based indentation level.

Source

pub fn set_numbering(&mut self, num_id: u32, level: u32) -> bool

Attach this paragraph to a list definition in place.

Returns false without mutation when level is outside Word’s supported range of 0 through 8.

Source

pub fn set_numbering_value(&mut self, numbering: Option<(u32, u32)>) -> bool

Set or clear this paragraph’s list numbering.

Returns false without mutation for an out-of-range level.

Source

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

Set space before the paragraph.

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

pub fn set_space_before(&mut self, length: Length)

Set space before the paragraph in place.

Source

pub fn set_space_before_value(&mut self, length: Option<Length>)

Set or clear direct space before the paragraph.

Source

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

Set space after the paragraph.

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

pub fn set_space_after(&mut self, length: Length)

Set space after the paragraph in place.

Source

pub fn set_space_after_value(&mut self, length: Option<Length>)

Set or clear direct space after the paragraph.

Source

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

Set left indentation.

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

pub fn set_indent_left(&mut self, length: Length)

Set left indentation in place.

Source

pub fn set_indent_left_value(&mut self, length: Option<Length>)

Set or clear direct left indentation.

Source

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

Set right indentation.

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

pub fn set_indent_right(&mut self, length: Length)

Set right indentation in place.

Source

pub fn set_indent_right_value(&mut self, length: Option<Length>)

Set or clear direct right indentation.

Source

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

Set first line indent.

Examples found in repository?
examples/generate_all_samples.rs (line 280)
97fn generate_feature_showcase(_samples_dir: &Path) -> Document {
98    let mut doc = Document::new();
99
100    // ── Page Setup & Metadata ──
101    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
102    doc.set_margins(
103        Length::inches(1.0),
104        Length::inches(1.0),
105        Length::inches(1.0),
106        Length::inches(1.0),
107    );
108    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
109    doc.set_gutter(Length::twips(0));
110    doc.set_title("rdocx Feature Showcase");
111    doc.set_author("rdocx Sample Generator");
112    doc.set_subject("Comprehensive feature demonstration");
113    doc.set_keywords("rdocx, docx, rust, sample, showcase");
114
115    // Headers & Footers with different first page
116    doc.set_different_first_page(true);
117    doc.set_first_page_header("rdocx");
118    doc.set_first_page_footer("Feature Showcase — Cover Page");
119    doc.set_header("rdocx Feature Showcase");
120    doc.set_footer("Generated by rdocx");
121
122    // ── COVER PAGE ──
123    let bg = create_sample_png(612, 792, [20, 45, 90]);
124    doc.add_background_image(&bg, "cover_bg.png");
125
126    for _ in 0..3 {
127        doc.add_paragraph("");
128    }
129    {
130        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
131        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
132    }
133    {
134        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
135        p.add_run("Complete Feature Showcase")
136            .size(28.0)
137            .color("FFFFFF")
138            .italic(true);
139    }
140    doc.add_paragraph("");
141    {
142        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
143        p.add_run("Every feature of the rdocx Rust library")
144            .size(13.0)
145            .color("B0C4DE");
146    }
147    {
148        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
149        p.add_run("demonstrated in a single document.")
150            .size(13.0)
151            .color("B0C4DE");
152    }
153
154    // ── TABLE OF CONTENTS ──
155    doc.add_paragraph("").page_break_before(true);
156    doc.insert_toc(doc.content_count(), 3);
157
158    // ── SECTION 1: TEXT FORMATTING ──
159    doc.add_paragraph("").page_break_before(true);
160    doc.add_paragraph("1. Text Formatting").style("Heading1");
161
162    // Paragraph Alignment
163    doc.add_paragraph("1.1 Paragraph Alignment")
164        .style("Heading2");
165    doc.add_paragraph("Left-aligned paragraph (default).")
166        .alignment(Alignment::Left);
167    doc.add_paragraph("Center-aligned paragraph.")
168        .alignment(Alignment::Center);
169    doc.add_paragraph("Right-aligned paragraph.")
170        .alignment(Alignment::Right);
171    doc.add_paragraph(
172        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
173         distributes extra space across word gaps so lines fill the full width of the text area.",
174    )
175    .alignment(Alignment::Justify);
176
177    doc.add_paragraph("");
178
179    // Run Formatting
180    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
181    {
182        let mut p = doc.add_paragraph("");
183        p.add_run("Bold").bold(true);
184        p.add_run(" | ");
185        p.add_run("Italic").italic(true);
186        p.add_run(" | ");
187        p.add_run("Bold Italic").bold(true).italic(true);
188        p.add_run(" | ");
189        p.add_run("Underline").underline(true);
190        p.add_run(" | ");
191        p.add_run("Strikethrough").strike(true);
192        p.add_run(" | ");
193        p.add_run("Double Strike").double_strike(true);
194    }
195    {
196        let mut p = doc.add_paragraph("");
197        p.add_run("Red").color("FF0000");
198        p.add_run(" | ");
199        p.add_run("Blue").color("0000FF");
200        p.add_run(" | ");
201        p.add_run("Green").color("00AA00");
202        p.add_run(" | ");
203        p.add_run("Highlighted").highlight("FFFF00");
204    }
205    {
206        let mut p = doc.add_paragraph("");
207        p.add_run("8pt").size(8.0);
208        p.add_run(" | ");
209        p.add_run("11pt").size(11.0);
210        p.add_run(" | ");
211        p.add_run("16pt").size(16.0);
212        p.add_run(" | ");
213        p.add_run("24pt").size(24.0);
214    }
215    {
216        let mut p = doc.add_paragraph("");
217        p.add_run("Arial").font("Arial");
218        p.add_run(" | ");
219        p.add_run("Times New Roman").font("Times New Roman");
220        p.add_run(" | ");
221        p.add_run("Courier New").font("Courier New");
222    }
223    {
224        let mut p = doc.add_paragraph("");
225        p.add_run("H");
226        p.add_run("2").subscript();
227        p.add_run("O (subscript) | E=mc");
228        p.add_run("2").superscript();
229        p.add_run(" (superscript)");
230    }
231    {
232        let mut p = doc.add_paragraph("");
233        p.add_run("ALL CAPS").all_caps(true);
234        p.add_run(" | ");
235        p.add_run("Small Caps").small_caps(true);
236        p.add_run(" | ");
237        p.add_run("Expanded").character_spacing(Length::twips(40));
238        p.add_run(" | ");
239        p.add_run("Hidden text (not visible)").hidden(true);
240    }
241
242    doc.add_paragraph("");
243
244    // Underline Styles
245    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
246    {
247        let mut p = doc.add_paragraph("");
248        p.add_run("Single ")
249            .underline_style(rdocx::UnderlineStyle::Single);
250        p.add_run("Double ")
251            .underline_style(rdocx::UnderlineStyle::Double);
252        p.add_run("Thick ")
253            .underline_style(rdocx::UnderlineStyle::Thick);
254        p.add_run("Dotted ")
255            .underline_style(rdocx::UnderlineStyle::Dotted);
256        p.add_run("Dash ")
257            .underline_style(rdocx::UnderlineStyle::Dash);
258        p.add_run("Wave ")
259            .underline_style(rdocx::UnderlineStyle::Wave);
260    }
261
262    // ── SECTION 2: PARAGRAPH FORMATTING ──
263    doc.add_paragraph("").page_break_before(true);
264    doc.add_paragraph("2. Paragraph Formatting")
265        .style("Heading1");
266
267    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
268    doc.add_paragraph("Paragraph with light green shading")
269        .shading("E2EFDA");
270    doc.add_paragraph("Paragraph with bottom border")
271        .border_bottom(BorderStyle::Single, 6, "2E75B6");
272    doc.add_paragraph("Paragraph with all borders (red)")
273        .border_all(BorderStyle::Single, 4, "FF0000");
274
275    doc.add_paragraph("2.2 Indentation").style("Heading2");
276    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
277        .indent_left(Length::inches(1.0))
278        .hanging_indent(Length::inches(0.5));
279    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
280        .first_line_indent(Length::inches(0.5));
281    doc.add_paragraph("Both left and right indent (0.75 inches each)")
282        .indent_left(Length::inches(0.75))
283        .indent_right(Length::inches(0.75));
284
285    doc.add_paragraph("2.3 Spacing & Line Height")
286        .style("Heading2");
287    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
288        .space_before(Length::pt(24.0))
289        .space_after(Length::pt(12.0));
290    doc.add_paragraph(
291        "Double line spacing paragraph. This text should have extra vertical space between \
292         lines to demonstrate line_spacing_multiple(2.0).",
293    )
294    .line_spacing_multiple(2.0);
295    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
296        .line_spacing(20.0);
297
298    doc.add_paragraph("2.4 Pagination Controls")
299        .style("Heading2");
300    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
301        .keep_with_next(true);
302    doc.add_paragraph("(This paragraph was kept with the one above.)");
303    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
304        .keep_together(true);
305    doc.add_paragraph("widow_control — prevents widow/orphan lines")
306        .widow_control(true);
307
308    // ── SECTION 3: LISTS ──
309    doc.add_paragraph("").page_break_before(true);
310    doc.add_paragraph("3. Lists").style("Heading1");
311
312    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
313    doc.add_bullet_list_item("First item", 0);
314    doc.add_bullet_list_item("Second item", 0);
315    doc.add_bullet_list_item("Nested level 1", 1);
316    doc.add_bullet_list_item("Nested level 2", 2);
317    doc.add_bullet_list_item("Back to level 1", 1);
318    doc.add_bullet_list_item("Third item", 0);
319
320    doc.add_paragraph("");
321
322    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
323    doc.add_numbered_list_item("First numbered item", 0);
324    doc.add_numbered_list_item("Second numbered item", 0);
325    doc.add_numbered_list_item("Sub-item A", 1);
326    doc.add_numbered_list_item("Sub-item B", 1);
327    doc.add_numbered_list_item("Third numbered item", 0);
328
329    // ── SECTION 4: TAB STOPS ──
330    doc.add_paragraph("");
331    doc.add_paragraph("4. Tab Stops").style("Heading1");
332
333    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
334    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
335        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
336        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
337        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
338        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
339
340    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
341    doc.add_paragraph("Item\t\tPrice")
342        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
343        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
344        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
345    doc.add_paragraph("Widget\t\t$19.99")
346        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
347        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
348        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
349    doc.add_paragraph("Gadget\t\t$249.50")
350        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
351        .add_tab_stop_with_leader(
352            TabAlignment::Right,
353            Length::inches(4.0),
354            TabLeader::Underscore,
355        )
356        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
357
358    // ── SECTION 5: TABLES ──
359    doc.add_paragraph("").page_break_before(true);
360    doc.add_paragraph("5. Tables").style("Heading1");
361
362    doc.add_paragraph("5.1 Basic Table").style("Heading2");
363    {
364        let mut tbl = doc
365            .add_table(4, 3)
366            .borders(BorderStyle::Single, 4, "000000");
367        for col in 0..3 {
368            tbl.cell(0, col).unwrap().shading("2E75B6");
369        }
370        tbl.cell(0, 0).unwrap().set_text("Name");
371        tbl.cell(0, 1).unwrap().set_text("Role");
372        tbl.cell(0, 2).unwrap().set_text("Location");
373        tbl.cell(1, 0).unwrap().set_text("Walter White");
374        tbl.cell(1, 1).unwrap().set_text("CEO");
375        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
376        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
377        tbl.cell(2, 1).unwrap().set_text("CTO");
378        tbl.cell(2, 2).unwrap().set_text("Remote");
379        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
380        tbl.cell(3, 1).unwrap().set_text("Security");
381        tbl.cell(3, 2).unwrap().set_text("Washington");
382    }
383
384    doc.add_paragraph("");
385
386    doc.add_paragraph("5.2 Column Span & Cell Shading")
387        .style("Heading2");
388    {
389        let mut tbl = doc
390            .add_table(3, 4)
391            .borders(BorderStyle::Single, 4, "000000")
392            .width_pct(100.0);
393        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
394        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
395        tbl.cell(1, 0).unwrap().set_text("Region");
396        tbl.cell(1, 0).unwrap().shading("D6E4F0");
397        tbl.cell(1, 1).unwrap().set_text("Q1");
398        tbl.cell(1, 1).unwrap().shading("D6E4F0");
399        tbl.cell(1, 2).unwrap().set_text("Q2");
400        tbl.cell(1, 2).unwrap().shading("D6E4F0");
401        tbl.cell(1, 3).unwrap().set_text("Total");
402        tbl.cell(1, 3).unwrap().shading("D6E4F0");
403        tbl.cell(2, 0).unwrap().set_text("Americas");
404        tbl.cell(2, 1).unwrap().set_text("$2.4M");
405        tbl.cell(2, 2).unwrap().set_text("$2.7M");
406        tbl.cell(2, 3).unwrap().set_text("$5.1M");
407    }
408
409    doc.add_paragraph("");
410
411    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
412    {
413        let mut tbl = doc
414            .add_table(4, 3)
415            .borders(BorderStyle::Single, 4, "000000");
416        tbl.cell(0, 0).unwrap().set_text("Category");
417        tbl.cell(0, 0).unwrap().shading("E2EFDA");
418        tbl.cell(0, 1).unwrap().set_text("Item");
419        tbl.cell(0, 1).unwrap().shading("E2EFDA");
420        tbl.cell(0, 2).unwrap().set_text("Price");
421        tbl.cell(0, 2).unwrap().shading("E2EFDA");
422        tbl.cell(1, 0).unwrap().set_text("Hardware");
423        tbl.cell(1, 0).unwrap().v_merge_restart();
424        tbl.cell(1, 1).unwrap().set_text("Laptop");
425        tbl.cell(1, 2).unwrap().set_text("$1,200");
426        tbl.cell(2, 0).unwrap().v_merge_continue();
427        tbl.cell(2, 1).unwrap().set_text("Monitor");
428        tbl.cell(2, 2).unwrap().set_text("$450");
429        tbl.cell(3, 0).unwrap().set_text("Software");
430        tbl.cell(3, 1).unwrap().set_text("IDE License");
431        tbl.cell(3, 2).unwrap().set_text("$200/yr");
432    }
433
434    doc.add_paragraph("");
435
436    doc.add_paragraph("5.4 Nested Table").style("Heading2");
437    {
438        let mut tbl = doc
439            .add_table(2, 2)
440            .borders(BorderStyle::Single, 6, "2E75B6");
441        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
442        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
443        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
444        {
445            let mut cell = tbl.cell(1, 1).unwrap();
446            cell.set_text("Nested table below:");
447            let mut nested = cell
448                .add_table(2, 2)
449                .borders(BorderStyle::Single, 2, "FF6600");
450            nested.cell(0, 0).unwrap().set_text("A");
451            nested.cell(0, 1).unwrap().set_text("B");
452            nested.cell(1, 0).unwrap().set_text("C");
453            nested.cell(1, 1).unwrap().set_text("D");
454        }
455    }
456
457    doc.add_paragraph("");
458
459    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
460        .style("Heading2");
461    {
462        let mut tbl = doc
463            .add_table(2, 3)
464            .borders(BorderStyle::Single, 4, "666666");
465        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
466        tbl.cell(0, 0).unwrap().set_text("Top");
467        tbl.cell(0, 0)
468            .unwrap()
469            .vertical_alignment(VerticalAlignment::Top)
470            .shading("FFF2CC");
471        tbl.cell(0, 1).unwrap().set_text("Center");
472        tbl.cell(0, 1)
473            .unwrap()
474            .vertical_alignment(VerticalAlignment::Center)
475            .shading("D9E2F3");
476        tbl.cell(0, 2).unwrap().set_text("Bottom");
477        tbl.cell(0, 2)
478            .unwrap()
479            .vertical_alignment(VerticalAlignment::Bottom)
480            .shading("E2EFDA");
481        tbl.row(1).unwrap().cant_split();
482        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
483        tbl.cell(1, 0).unwrap().no_wrap();
484        tbl.cell(1, 1).unwrap().set_text("Fixed width");
485        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
486        tbl.cell(1, 2).unwrap().set_text("Normal");
487    }
488
489    // ── SECTION 6: IMAGES ──
490    doc.add_paragraph("").page_break_before(true);
491    doc.add_paragraph("6. Images").style("Heading1");
492
493    doc.add_paragraph("6.1 Inline Image").style("Heading2");
494    doc.add_paragraph("A blue gradient image below:");
495    let img = create_sample_png(200, 50, [0, 80, 200]);
496    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
497
498    doc.add_paragraph("");
499    doc.add_paragraph("6.2 Header Image").style("Heading2");
500    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
501    doc.set_header_image(
502        &hdr_img,
503        "header_logo.png",
504        Length::inches(2.0),
505        Length::inches(0.2),
506    );
507    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
508
509    doc.add_paragraph("");
510    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
511
512    // ── SECTION 7: CONTENT MANIPULATION ──
513    doc.add_paragraph("").page_break_before(true);
514    doc.add_paragraph("7. Content Manipulation")
515        .style("Heading1");
516
517    doc.add_paragraph("7.1 Placeholder Replacement")
518        .style("Heading2");
519    doc.add_paragraph("Customer: {{customer}}");
520    doc.add_paragraph("Date: {{date}}");
521    doc.add_paragraph("Reference: {{ref_number}}");
522    {
523        let mut tbl = doc
524            .add_table(2, 2)
525            .borders(BorderStyle::Single, 4, "000000");
526        tbl.cell(0, 0).unwrap().set_text("Project");
527        tbl.cell(0, 0).unwrap().shading("D6E4F0");
528        tbl.cell(0, 1).unwrap().set_text("{{project}}");
529        tbl.cell(1, 0).unwrap().set_text("Status");
530        tbl.cell(1, 0).unwrap().shading("D6E4F0");
531        tbl.cell(1, 1).unwrap().set_text("{{status}}");
532    }
533
534    let mut replacements = HashMap::new();
535    replacements.insert("{{customer}}", "Tensorbee Inc.");
536    replacements.insert("{{date}}", "February 22, 2026");
537    replacements.insert("{{ref_number}}", "TB-2026-001");
538    replacements.insert("{{project}}", "Infrastructure Upgrade");
539    replacements.insert("{{status}}", "In Progress");
540    let n = doc.replace_all(&replacements);
541    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
542
543    doc.add_paragraph("");
544
545    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
546    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
547    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
548    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
549
550    doc.add_paragraph("");
551
552    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
553    doc.add_paragraph("Section A: First content.");
554    doc.add_paragraph("Section C: Third content.");
555    if let Some(idx) = doc.find_content_index("Section C") {
556        doc.insert_paragraph(
557            idx,
558            "Section B: Inserted between A and C via find_content_index().",
559        );
560    }
561
562    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
563    doc.add_paragraph("").section_break(SectionBreak::NextPage);
564    doc.add_paragraph("8. Section Breaks & Orientation")
565        .style("Heading1");
566    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
567
568    {
569        let mut tbl = doc
570            .add_table(3, 7)
571            .borders(BorderStyle::Single, 4, "2E75B6");
572        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
573        for (c, h) in headers.iter().enumerate() {
574            tbl.cell(0, c).unwrap().set_text(h);
575            tbl.cell(0, c).unwrap().shading("2E75B6");
576        }
577        let data = [
578            [
579                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
580            ],
581            [
582                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
583            ],
584        ];
585        for (r, row) in data.iter().enumerate() {
586            for (c, v) in row.iter().enumerate() {
587                tbl.cell(r + 1, c).unwrap().set_text(v);
588            }
589        }
590    }
591
592    doc.add_paragraph("")
593        .section_break(SectionBreak::NextPage)
594        .section_landscape();
595
596    // ── SECTION 9: CUSTOM STYLES ──
597    doc.add_paragraph("9. Custom Styles").style("Heading1");
598    doc.add_style(
599        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
600            .based_on("Normal")
601            .paragraph_properties(rdocx_oxml::properties::CT_PPr {
602                shading: Some(rdocx_oxml::properties::CT_Shd {
603                    val: "clear".to_string(),
604                    color: None,
605                    fill: Some("FFF2CC".to_string()),
606                }),
607                ..Default::default()
608            })
609            .run_properties(rdocx_oxml::properties::CT_RPr {
610                bold: Some(true),
611                color: Some("C45911".to_string()),
612                ..Default::default()
613            }),
614    );
615    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
616        .style("CustomHighlight");
617
618    doc.add_paragraph("");
619
620    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
621    doc.add_paragraph("10. Document Intelligence API")
622        .style("Heading1");
623
624    let wc = doc.word_count();
625    let hc = doc.headings().len();
626    let ic = doc.images().len();
627    let lc = doc.links().len();
628    doc.add_paragraph(&format!("Word count: {wc}"));
629    doc.add_paragraph(&format!("Heading count: {hc}"));
630    doc.add_paragraph(&format!("Image count: {ic}"));
631    doc.add_paragraph(&format!("Link count: {lc}"));
632
633    let outline = doc.document_outline();
634    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
635
636    let issues = doc.audit_accessibility();
637    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
638    for issue in &issues {
639        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
640    }
641
642    // ── SECTION 11: DOCUMENT MERGING ──
643    doc.add_paragraph("");
644    doc.add_paragraph("11. Document Merging").style("Heading1");
645    {
646        let mut other = Document::new();
647        other.add_paragraph("This paragraph was merged from another document using append().");
648        doc.append(&other);
649    }
650
651    doc.add_paragraph("");
652
653    // ── SUMMARY ──
654    doc.add_paragraph("Summary of Demonstrated Features")
655        .style("Heading1");
656    let features = [
657        "Page setup: size, margins, header/footer distance, gutter",
658        "Document metadata: title, author, subject, keywords",
659        "Headers and footers: text, images, different first page",
660        "Background images (full-page behind text)",
661        "Table of Contents generation with bookmarks",
662        "Text formatting: bold, italic, underline styles, strike, color, size, font",
663        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
664        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
665        "Pagination controls: keep-with-next, keep-together, widow control",
666        "Bullet and numbered lists with nesting",
667        "Tab stops with dot/underscore/hyphen leaders",
668        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
669        "Row properties: header rows, exact height, cant-split",
670        "Cell properties: width, no-wrap, vertical alignment",
671        "Inline images and header images",
672        "Placeholder replacement (single and batch)",
673        "Regex-based find and replace",
674        "Content insertion at specific positions",
675        "Section breaks with mixed portrait/landscape",
676        "Custom paragraph and character styles",
677        "Document intelligence: word count, headings, outline, images, links",
678        "Accessibility audit",
679        "Document merging (append)",
680        "Export: DOCX, PDF, HTML, Markdown",
681    ];
682    for f in &features {
683        doc.add_bullet_list_item(f, 0);
684    }
685    doc.add_paragraph("");
686    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
687        .alignment(Alignment::Center)
688        .shading("E2EFDA")
689        .border_all(BorderStyle::Single, 2, "00AA00");
690
691    doc
692}
693
694// =============================================================================
695// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
696// =============================================================================
697fn generate_proposal(_samples_dir: &Path) -> Document {
698    let mut doc = Document::new();
699
700    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
701    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
702    doc.set_margins(
703        Length::inches(1.0),
704        Length::inches(1.0),
705        Length::inches(1.0),
706        Length::inches(1.0),
707    );
708    doc.set_title("AI Platform Modernization Proposal");
709    doc.set_author("Walter White");
710    doc.set_subject("Technology Proposal");
711    doc.set_keywords("proposal, AI, modernization, Tensorbee");
712
713    // Banner header
714    let logo_img = create_logo_png(220, 48);
715    let banner = build_header_banner_xml(
716        "rId1",
717        &BannerOpts {
718            bg_color: "1B2A4A",
719            banner_width: 7772400,
720            banner_height: 914400,
721            logo_width: 2011680,
722            logo_height: 438912,
723            logo_x_offset: 295125,
724            logo_y_offset: 237744,
725        },
726    );
727    doc.set_raw_header_with_images(
728        banner,
729        &[("rId1", &logo_img, "logo.png")],
730        rdocx_oxml::header_footer::HdrFtrType::Default,
731    );
732    doc.set_different_first_page(true);
733    let cover_banner = build_header_banner_xml(
734        "rId1",
735        &BannerOpts {
736            bg_color: "C5922E",
737            banner_width: 7772400,
738            banner_height: 914400,
739            logo_width: 2011680,
740            logo_height: 438912,
741            logo_x_offset: 295125,
742            logo_y_offset: 237744,
743        },
744    );
745    doc.set_raw_header_with_images(
746        cover_banner,
747        &[("rId1", &logo_img, "logo.png")],
748        rdocx_oxml::header_footer::HdrFtrType::First,
749    );
750    doc.set_margins(
751        Length::twips(2292),
752        Length::twips(1440),
753        Length::twips(1440),
754        Length::twips(1440),
755    );
756    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
757    doc.set_footer("Tensorbee — Confidential");
758
759    // Custom styles
760    doc.add_style(
761        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
762            .based_on("Normal")
763            .run_properties(rdocx_oxml::properties::CT_RPr {
764                bold: Some(true),
765                font_ascii: Some("Georgia".to_string()),
766                font_hansi: Some("Georgia".to_string()),
767                sz: Some(rdocx_oxml::HalfPoint(56)), // half-points → 28pt
768                color: Some("1B2A4A".to_string()),
769                ..Default::default()
770            }),
771    );
772
773    // ── Cover Page ──
774    for _ in 0..4 {
775        doc.add_paragraph("");
776    }
777    doc.add_paragraph("AI Platform Modernization")
778        .style("ProposalTitle")
779        .alignment(Alignment::Center);
780    doc.add_paragraph("")
781        .alignment(Alignment::Center)
782        .border_bottom(BorderStyle::Single, 8, "C5922E");
783    {
784        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
785        p.add_run("Prepared for Global Financial Corp.")
786            .size(14.0)
787            .color("666666")
788            .italic(true);
789    }
790    {
791        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
792        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
793            .size(12.0)
794            .color("888888");
795    }
796    {
797        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
798        p.add_run("February 22, 2026").size(12.0).color("888888");
799    }
800
801    // ── TOC ──
802    doc.add_paragraph("").page_break_before(true);
803    doc.add_paragraph("Table of Contents").style("Heading1");
804    // We'll add a manual-style TOC since the insert_toc goes at a specific position
805    doc.insert_toc(doc.content_count(), 2);
806
807    // ── Executive Summary ──
808    doc.add_paragraph("").page_break_before(true);
809    doc.add_paragraph("Executive Summary").style("Heading1");
810    doc.add_paragraph(
811        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
812         transitioning from legacy batch-processing systems to a real-time inference architecture. \
813         This transformation will reduce model deployment time from weeks to hours, improve \
814         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
815    )
816    .first_line_indent(Length::inches(0.3));
817    doc.add_paragraph(
818        "The project spans three phases over 18 months, with a total investment of $2.8M. \
819         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
820         similar transformations for three Fortune 500 financial institutions.",
821    )
822    .first_line_indent(Length::inches(0.3));
823
824    // Key metrics table
825    doc.add_paragraph("");
826    {
827        let mut tbl = doc
828            .add_table(5, 2)
829            .borders(BorderStyle::Single, 4, "1B2A4A")
830            .width_pct(80.0);
831        tbl.cell(0, 0).unwrap().set_text("Key Metric");
832        tbl.cell(0, 0).unwrap().shading("1B2A4A");
833        tbl.cell(0, 1).unwrap().set_text("Value");
834        tbl.cell(0, 1).unwrap().shading("1B2A4A");
835        let rows = [
836            ("Total Investment", "$2.8M"),
837            ("Annual Savings", "$4.2M"),
838            ("ROI (Year 1)", "150%"),
839            ("Timeline", "18 months"),
840        ];
841        for (i, (k, v)) in rows.iter().enumerate() {
842            tbl.cell(i + 1, 0).unwrap().set_text(k);
843            if i % 2 == 0 {
844                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
845            }
846            tbl.cell(i + 1, 1).unwrap().set_text(v);
847            if i % 2 == 0 {
848                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
849            }
850        }
851    }
852
853    // ── Problem Statement ──
854    doc.add_paragraph("").page_break_before(true);
855    doc.add_paragraph("Problem Statement").style("Heading1");
856    doc.add_paragraph(
857        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
858    );
859    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);
860    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
861    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
862
863    // ── Proposed Solution ──
864    doc.add_paragraph("").page_break_before(true);
865    doc.add_paragraph("Proposed Solution").style("Heading1");
866
867    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
868        .style("Heading2");
869    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
870    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
871    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
872
873    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
874        .style("Heading2");
875    doc.add_bullet_list_item(
876        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
877        0,
878    );
879    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
880    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
881
882    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
883        .style("Heading2");
884    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
885    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
886    doc.add_bullet_list_item(
887        "Self-service model deployment portal for data science teams",
888        0,
889    );
890
891    // ── Budget ──
892    doc.add_paragraph("Budget Breakdown").style("Heading1");
893    {
894        let mut tbl = doc
895            .add_table(6, 3)
896            .borders(BorderStyle::Single, 4, "1B2A4A")
897            .width_pct(100.0);
898        tbl.cell(0, 0).unwrap().set_text("Category");
899        tbl.cell(0, 0).unwrap().shading("1B2A4A");
900        tbl.cell(0, 1).unwrap().set_text("Cost");
901        tbl.cell(0, 1).unwrap().shading("1B2A4A");
902        tbl.cell(0, 2).unwrap().set_text("Notes");
903        tbl.cell(0, 2).unwrap().shading("1B2A4A");
904        let items = [
905            (
906                "Infrastructure",
907                "$450,000",
908                "Cloud compute + GPU instances",
909            ),
910            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
911            (
912                "Software Licenses",
913                "$350,000",
914                "Kafka, monitoring, CI/CD tools",
915            ),
916            (
917                "Training & Enablement",
918                "$200,000",
919                "Team training, documentation",
920            ),
921            ("Contingency (10%)", "$200,000", "Risk buffer"),
922        ];
923        for (i, (cat, cost, note)) in items.iter().enumerate() {
924            tbl.cell(i + 1, 0).unwrap().set_text(cat);
925            tbl.cell(i + 1, 1).unwrap().set_text(cost);
926            tbl.cell(i + 1, 2).unwrap().set_text(note);
927            if i % 2 == 0 {
928                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
929                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
930                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
931            }
932        }
933    }
934
935    // ── Closing ──
936    doc.add_paragraph("");
937    doc.add_paragraph("Next Steps").style("Heading1");
938    doc.add_numbered_list_item(
939        "Schedule technical deep-dive with GFC engineering team (Week 1)",
940        0,
941    );
942    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
943    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
944
945    doc.add_paragraph("");
946    {
947        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
948        p.add_run("Walter White")
949            .bold(true)
950            .size(14.0)
951            .color("1B2A4A");
952    }
953    {
954        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
955        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
956            .size(10.0)
957            .color("888888");
958    }
959
960    doc
961}
962
963// =============================================================================
964// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
965// =============================================================================
966fn generate_quote(_samples_dir: &Path) -> Document {
967    let mut doc = Document::new();
968
969    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
970    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
971    doc.set_margins(
972        Length::inches(0.75),
973        Length::inches(0.75),
974        Length::inches(0.75),
975        Length::inches(0.75),
976    );
977    doc.set_title("Quotation QT-2026-0147");
978    doc.set_author("Walter White");
979    doc.set_keywords("quote, BOM, Tensorbee");
980
981    doc.set_header("Tensorbee — Quotation");
982    doc.set_footer("QT-2026-0147 | Page");
983
984    // ── Header Block ──
985    {
986        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
987        p.add_run("TENSORBEE")
988            .bold(true)
989            .size(28.0)
990            .color("008B8B")
991            .font("Helvetica");
992    }
993    doc.add_paragraph("123 Innovation Drive, Suite 400")
994        .alignment(Alignment::Left);
995    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
996        .alignment(Alignment::Left);
997    doc.add_paragraph("")
998        .border_bottom(BorderStyle::Single, 8, "008B8B");
999
1000    // Quote meta
1001    doc.add_paragraph("");
1002    {
1003        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
1004        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
1005        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
1006        tbl.cell(0, 2).unwrap().set_text("DATE");
1007        tbl.cell(0, 2).unwrap().shading("008B8B");
1008        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1009        tbl.cell(0, 3).unwrap().shading("008B8B");
1010        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1011        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1012        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1013        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1014        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1015        tbl.cell(2, 1).unwrap().set_text("Walter White");
1016        tbl.cell(2, 2).unwrap().set_text("Payment:");
1017        tbl.cell(2, 3).unwrap().set_text("Net 30");
1018        tbl.cell(3, 0).unwrap().set_text("Customer:");
1019        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1020        tbl.cell(3, 2).unwrap().set_text("Currency:");
1021        tbl.cell(3, 3).unwrap().set_text("USD");
1022    }
1023
1024    doc.add_paragraph("");
1025
1026    // ── Bill of Materials ──
1027    {
1028        let mut p = doc.add_paragraph("");
1029        p.add_run("Bill of Materials")
1030            .bold(true)
1031            .size(16.0)
1032            .color("1A3C3C");
1033    }
1034    doc.add_paragraph("");
1035
1036    {
1037        let mut tbl = doc
1038            .add_table(10, 6)
1039            .borders(BorderStyle::Single, 2, "008B8B")
1040            .width_pct(100.0)
1041            .layout_fixed();
1042        // Headers
1043        let hdrs = [
1044            "#",
1045            "Part Number",
1046            "Description",
1047            "Qty",
1048            "Unit Price",
1049            "Total",
1050        ];
1051        for (c, h) in hdrs.iter().enumerate() {
1052            tbl.cell(0, c).unwrap().set_text(h);
1053            tbl.cell(0, c).unwrap().shading("008B8B");
1054        }
1055
1056        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1057            (
1058                "TB-GPU-A100",
1059                "NVIDIA A100 80GB GPU",
1060                "4",
1061                "$12,500.00",
1062                "$50,000.00",
1063            ),
1064            (
1065                "TB-SRV-R750",
1066                "Dell R750xa Server Chassis",
1067                "2",
1068                "$8,200.00",
1069                "$16,400.00",
1070            ),
1071            (
1072                "TB-RAM-256G",
1073                "256GB DDR5 ECC Memory Module",
1074                "8",
1075                "$890.00",
1076                "$7,120.00",
1077            ),
1078            (
1079                "TB-SSD-3840",
1080                "3.84TB NVMe U.2 SSD",
1081                "8",
1082                "$1,150.00",
1083                "$9,200.00",
1084            ),
1085            (
1086                "TB-NET-CX7",
1087                "ConnectX-7 200GbE NIC",
1088                "4",
1089                "$1,800.00",
1090                "$7,200.00",
1091            ),
1092            (
1093                "TB-SW-48P",
1094                "48-Port 100GbE Switch",
1095                "1",
1096                "$22,000.00",
1097                "$22,000.00",
1098            ),
1099            (
1100                "TB-CAB-RACK",
1101                "42U Server Rack + PDU",
1102                "1",
1103                "$4,500.00",
1104                "$4,500.00",
1105            ),
1106            (
1107                "TB-SVC-INST",
1108                "Installation & Configuration",
1109                "1",
1110                "$8,500.00",
1111                "$8,500.00",
1112            ),
1113            (
1114                "TB-SVC-SUPP",
1115                "3-Year Premium Support",
1116                "1",
1117                "$15,000.00",
1118                "$15,000.00",
1119            ),
1120        ];
1121
1122        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1123            let row = i + 1;
1124            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1125            tbl.cell(row, 1).unwrap().set_text(pn);
1126            tbl.cell(row, 2).unwrap().set_text(desc);
1127            tbl.cell(row, 3).unwrap().set_text(qty);
1128            tbl.cell(row, 4).unwrap().set_text(unit);
1129            tbl.cell(row, 5).unwrap().set_text(total);
1130            if i % 2 == 0 {
1131                for c in 0..6 {
1132                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1133                }
1134            }
1135        }
1136    }
1137
1138    doc.add_paragraph("");
1139
1140    // ── Totals ──
1141    {
1142        let mut tbl = doc
1143            .add_table(4, 2)
1144            .borders(BorderStyle::Single, 2, "008B8B")
1145            .width(Length::inches(3.5))
1146            .alignment(Alignment::Right);
1147        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1148        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1149        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1150        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1151        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1152        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1153        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1154        tbl.cell(3, 0).unwrap().shading("E07020");
1155        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1156        tbl.cell(3, 1).unwrap().shading("E07020");
1157    }
1158
1159    doc.add_paragraph("");
1160
1161    // ── Terms & Conditions ──
1162    {
1163        let mut p = doc.add_paragraph("");
1164        p.add_run("Terms & Conditions")
1165            .bold(true)
1166            .size(14.0)
1167            .color("1A3C3C");
1168    }
1169    doc.add_numbered_list_item(
1170        "This quotation is valid for 30 calendar days from the date of issue.",
1171        0,
1172    );
1173    doc.add_numbered_list_item(
1174        "All prices are in USD and exclusive of applicable taxes unless stated.",
1175        0,
1176    );
1177    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1178    doc.add_numbered_list_item(
1179        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1180        0,
1181    );
1182    doc.add_numbered_list_item(
1183        "Warranty: 3-year manufacturer warranty on all hardware components.",
1184        0,
1185    );
1186    doc.add_numbered_list_item(
1187        "Returns subject to 15% restocking fee if initiated after 14 days.",
1188        0,
1189    );
1190
1191    doc.add_paragraph("");
1192    doc.add_paragraph("")
1193        .border_bottom(BorderStyle::Single, 4, "008B8B");
1194    doc.add_paragraph("");
1195    {
1196        let mut p = doc.add_paragraph("");
1197        p.add_run("Accepted by: ").bold(true);
1198        p.add_run("___________________________ Date: ___________");
1199    }
1200    {
1201        let mut p = doc.add_paragraph("");
1202        p.add_run("Print Name: ").bold(true);
1203        p.add_run("___________________________ Title: ___________");
1204    }
1205
1206    doc
1207}
1208
1209// =============================================================================
1210// 4. INVOICE — Crimson + charcoal scheme
1211// =============================================================================
1212fn generate_invoice(_samples_dir: &Path) -> Document {
1213    let mut doc = Document::new();
1214
1215    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1216    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1217    doc.set_margins(
1218        Length::inches(0.75),
1219        Length::inches(0.75),
1220        Length::inches(0.75),
1221        Length::inches(0.75),
1222    );
1223    doc.set_title("Invoice INV-2026-0392");
1224    doc.set_author("Walter White");
1225    doc.set_keywords("invoice, Tensorbee");
1226
1227    doc.set_footer("Tensorbee — Thank you for your business!");
1228
1229    // ── Company & Invoice Header ──
1230    {
1231        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1232        // Company name left, INVOICE right
1233        {
1234            let mut cell = tbl.cell(0, 0).unwrap();
1235            let mut p = cell.add_paragraph("");
1236            p.add_run("TENSORBEE")
1237                .bold(true)
1238                .size(32.0)
1239                .color("B22222")
1240                .font("Helvetica");
1241        }
1242        {
1243            let mut cell = tbl.cell(0, 1).unwrap();
1244            let mut p = cell.add_paragraph("");
1245            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1246        }
1247        tbl.cell(1, 0)
1248            .unwrap()
1249            .set_text("123 Innovation Drive, Suite 400");
1250        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1251        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1252        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1253        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1254        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1255    }
1256
1257    doc.add_paragraph("")
1258        .border_bottom(BorderStyle::Thick, 8, "B22222");
1259    doc.add_paragraph("");
1260
1261    // ── Bill To / Ship To ──
1262    {
1263        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1264        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1265        tbl.cell(0, 0).unwrap().shading("B22222");
1266        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1267        tbl.cell(0, 1).unwrap().shading("B22222");
1268        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1269        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1270        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1271        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1272        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1273        tbl.cell(3, 1)
1274            .unwrap()
1275            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1276    }
1277
1278    doc.add_paragraph("");
1279
1280    // ── Line Items ──
1281    {
1282        let mut tbl = doc
1283            .add_table(8, 5)
1284            .borders(BorderStyle::Single, 2, "B22222")
1285            .width_pct(100.0);
1286        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1287        for (c, h) in hdrs.iter().enumerate() {
1288            tbl.cell(0, c).unwrap().set_text(h);
1289            tbl.cell(0, c).unwrap().shading("B22222");
1290        }
1291        let items = [
1292            (
1293                "ML Infrastructure Setup — Phase 1",
1294                "1",
1295                "$45,000.00",
1296                "$3,881.25",
1297                "$48,881.25",
1298            ),
1299            (
1300                "Data Pipeline Development (160 hrs)",
1301                "160",
1302                "$225.00",
1303                "$3,105.00",
1304                "$39,105.00",
1305            ),
1306            (
1307                "GPU Cluster Configuration",
1308                "1",
1309                "$12,000.00",
1310                "$1,035.00",
1311                "$13,035.00",
1312            ),
1313            (
1314                "API Gateway Implementation",
1315                "1",
1316                "$18,500.00",
1317                "$1,595.63",
1318                "$20,095.63",
1319            ),
1320            (
1321                "Load Testing & QA (80 hrs)",
1322                "80",
1323                "$195.00",
1324                "$1,345.50",
1325                "$16,945.50",
1326            ),
1327            (
1328                "Documentation & Training",
1329                "1",
1330                "$8,000.00",
1331                "$690.00",
1332                "$8,690.00",
1333            ),
1334            (
1335                "Project Management (3 months)",
1336                "3",
1337                "$6,500.00",
1338                "$1,679.63",
1339                "$21,179.63",
1340            ),
1341        ];
1342        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1343            let r = i + 1;
1344            tbl.cell(r, 0).unwrap().set_text(desc);
1345            tbl.cell(r, 1).unwrap().set_text(qty);
1346            tbl.cell(r, 2).unwrap().set_text(unit);
1347            tbl.cell(r, 3).unwrap().set_text(tax);
1348            tbl.cell(r, 4).unwrap().set_text(amt);
1349            if i % 2 == 0 {
1350                for c in 0..5 {
1351                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1352                }
1353            }
1354        }
1355    }
1356
1357    doc.add_paragraph("");
1358
1359    // ── Totals ──
1360    {
1361        let mut tbl = doc
1362            .add_table(4, 2)
1363            .borders(BorderStyle::Single, 2, "B22222")
1364            .width(Length::inches(3.0))
1365            .alignment(Alignment::Right);
1366        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1367        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1368        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1369        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1370        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1371        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1372        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1373        tbl.cell(3, 0).unwrap().shading("B22222");
1374        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1375        tbl.cell(3, 1).unwrap().shading("B22222");
1376    }
1377
1378    doc.add_paragraph("");
1379    doc.add_paragraph("");
1380
1381    // ── Payment Details ──
1382    {
1383        let mut p = doc.add_paragraph("");
1384        p.add_run("Payment Details")
1385            .bold(true)
1386            .size(14.0)
1387            .color("333333");
1388    }
1389    doc.add_paragraph("Bank: Silicon Valley Bank")
1390        .indent_left(Length::inches(0.3));
1391    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1392        .indent_left(Length::inches(0.3));
1393    doc.add_paragraph("Routing: 121140399")
1394        .indent_left(Length::inches(0.3));
1395    doc.add_paragraph("Swift: SVBKUS6S")
1396        .indent_left(Length::inches(0.3));
1397
1398    doc.add_paragraph("");
1399
1400    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1401        .shading("FAF0F0")
1402        .border_all(BorderStyle::Single, 2, "B22222");
1403
1404    doc
1405}
1406
1407// =============================================================================
1408// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1409// =============================================================================
1410fn generate_report(_samples_dir: &Path) -> Document {
1411    let mut doc = Document::new();
1412
1413    // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1414    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1415    doc.set_margins(
1416        Length::inches(1.0),
1417        Length::inches(1.0),
1418        Length::inches(1.0),
1419        Length::inches(1.0),
1420    );
1421    doc.set_title("Q4 2025 Environmental Impact Report");
1422    doc.set_author("Walter White");
1423    doc.set_subject("Quarterly Environmental Report");
1424    doc.set_keywords("environment, sustainability, report, Tensorbee");
1425
1426    doc.set_different_first_page(true);
1427    doc.set_first_page_header("");
1428    doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1429    doc.set_footer("CONFIDENTIAL — Page");
1430
1431    // ── Cover ──
1432    let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1433    doc.add_background_image(&cover_bg, "report_cover.png");
1434
1435    for _ in 0..5 {
1436        doc.add_paragraph("");
1437    }
1438    {
1439        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440        p.add_run("Q4 2025")
1441            .bold(true)
1442            .size(48.0)
1443            .color("FFFFFF")
1444            .font("Georgia");
1445    }
1446    {
1447        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1448        p.add_run("Environmental Impact Report")
1449            .size(24.0)
1450            .color("90EE90")
1451            .font("Georgia")
1452            .italic(true);
1453    }
1454    doc.add_paragraph("");
1455    {
1456        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1457        p.add_run("Tensorbee — Sustainability Division")
1458            .size(14.0)
1459            .color("C0C0C0");
1460    }
1461    {
1462        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1463        p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1464            .size(11.0)
1465            .color("AAAAAA");
1466    }
1467
1468    // ── TOC ──
1469    doc.add_paragraph("").page_break_before(true);
1470    doc.insert_toc(doc.content_count(), 3);
1471
1472    // ── Section 1: Executive Overview ──
1473    doc.add_paragraph("").page_break_before(true);
1474    doc.add_paragraph("Executive Overview").style("Heading1");
1475    doc.add_paragraph(
1476        "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1477         sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1478         to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1479         100% renewable energy in our primary data centers and the launch of our carbon offset \
1480         marketplace.",
1481    )
1482    .first_line_indent(Length::inches(0.3));
1483
1484    // Image: performance chart
1485    let chart_img = create_chart_png(400, 200);
1486    doc.add_paragraph("");
1487    doc.add_picture(
1488        &chart_img,
1489        "performance_chart.png",
1490        Length::inches(5.0),
1491        Length::inches(2.5),
1492    )
1493    .alignment(Alignment::Center);
1494    {
1495        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1496        p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1497            .italic(true)
1498            .size(9.0)
1499            .color("666666");
1500    }
1501
1502    // ── Section 2: Energy Consumption ──
1503    doc.add_paragraph("").page_break_before(true);
1504    doc.add_paragraph("Energy Consumption").style("Heading1");
1505
1506    doc.add_paragraph("Data Center Operations")
1507        .style("Heading2");
1508    doc.add_paragraph(
1509        "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1510         a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1511         server consolidation.",
1512    );
1513
1514    // Data table
1515    {
1516        let mut tbl = doc
1517            .add_table(5, 4)
1518            .borders(BorderStyle::Single, 4, "2D5016")
1519            .width_pct(100.0);
1520        let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1521        for (c, h) in hdrs.iter().enumerate() {
1522            tbl.cell(0, c).unwrap().set_text(h);
1523            tbl.cell(0, c).unwrap().shading("2D5016");
1524        }
1525        let rows = [
1526            ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1527            ("Dublin (EU)", "2.1", "1.2", "1.18"),
1528            ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1529            ("TOTAL", "7.1", "4.2", "1.17 avg"),
1530        ];
1531        for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1532            tbl.cell(i + 1, 0).unwrap().set_text(dc);
1533            tbl.cell(i + 1, 1).unwrap().set_text(cap);
1534            tbl.cell(i + 1, 2).unwrap().set_text(use_);
1535            tbl.cell(i + 1, 3).unwrap().set_text(pue);
1536            if i == 3 {
1537                for c in 0..4 {
1538                    tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1539                }
1540            }
1541        }
1542    }
1543
1544    doc.add_paragraph("");
1545
1546    doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1547    doc.add_paragraph("Breakdown of energy sources across all facilities:");
1548
1549    doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1550    doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1551    doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1552    doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1553
1554    // ── Section 3: Water & Waste ──
1555    doc.add_paragraph("Water & Waste Management")
1556        .style("Heading1");
1557
1558    doc.add_paragraph("Water Usage").style("Heading2");
1559    doc.add_paragraph(
1560        "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1561         Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1562    );
1563
1564    doc.add_paragraph("Waste Reduction").style("Heading2");
1565    doc.add_paragraph("E-waste management results for Q4:")
1566        .keep_with_next(true);
1567    {
1568        let mut tbl = doc
1569            .add_table(4, 3)
1570            .borders(BorderStyle::Single, 2, "6B8E23")
1571            .width_pct(80.0);
1572        tbl.cell(0, 0).unwrap().set_text("Category");
1573        tbl.cell(0, 0).unwrap().shading("6B8E23");
1574        tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1575        tbl.cell(0, 1).unwrap().shading("6B8E23");
1576        tbl.cell(0, 2).unwrap().set_text("Recycled %");
1577        tbl.cell(0, 2).unwrap().shading("6B8E23");
1578        let rows = [
1579            ("Server Hardware", "2,450", "94%"),
1580            ("Networking Equipment", "820", "91%"),
1581            ("Storage Media", "340", "99%"),
1582        ];
1583        for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1584            tbl.cell(i + 1, 0).unwrap().set_text(cat);
1585            tbl.cell(i + 1, 1).unwrap().set_text(wt);
1586            tbl.cell(i + 1, 2).unwrap().set_text(pct);
1587        }
1588    }
1589
1590    // ── Section 4: Initiatives ──
1591    doc.add_paragraph("").page_break_before(true);
1592    doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1593
1594    doc.add_paragraph("Planned Programs").style("Heading2");
1595    doc.add_numbered_list_item(
1596        "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1597        0,
1598    );
1599    doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1600    doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1601    doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1602
1603    doc.add_paragraph("Investment Targets").style("Heading2");
1604    doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1605        .keep_with_next(true);
1606    {
1607        let mut tbl = doc
1608            .add_table(5, 2)
1609            .borders(BorderStyle::Single, 4, "2D5016");
1610        tbl.cell(0, 0).unwrap().set_text("Initiative");
1611        tbl.cell(0, 0).unwrap().shading("2D5016");
1612        tbl.cell(0, 1).unwrap().set_text("Budget");
1613        tbl.cell(0, 1).unwrap().shading("2D5016");
1614        let rows = [
1615            ("Battery Storage", "$1.2M"),
1616            ("Immersion Cooling Pilot", "$800K"),
1617            ("Solar Panel Expansion", "$2.1M"),
1618            ("Carbon Credits", "$500K"),
1619        ];
1620        for (i, (init, budget)) in rows.iter().enumerate() {
1621            tbl.cell(i + 1, 0).unwrap().set_text(init);
1622            tbl.cell(i + 1, 1).unwrap().set_text(budget);
1623            if i % 2 == 0 {
1624                tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1625                tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1626            }
1627        }
1628    }
1629
1630    // Image: sustainability roadmap
1631    let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1632    doc.add_paragraph("");
1633    doc.add_picture(
1634        &roadmap_img,
1635        "roadmap.png",
1636        Length::inches(6.0),
1637        Length::inches(1.2),
1638    )
1639    .alignment(Alignment::Center);
1640    {
1641        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1642        p.add_run("Figure 2: 2026 Sustainability Roadmap")
1643            .italic(true)
1644            .size(9.0)
1645            .color("666666");
1646    }
1647
1648    doc.add_paragraph("");
1649    doc.add_paragraph(
1650        "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1651    )
1652    .shading("FFFAF0")
1653    .border_all(BorderStyle::Single, 2, "2D5016");
1654
1655    doc
1656}
1657
1658// =============================================================================
1659// 6. LETTER — Slate blue + silver scheme
1660// =============================================================================
1661fn generate_letter(_samples_dir: &Path) -> Document {
1662    let mut doc = Document::new();
1663
1664    // Colors: Slate #4A5568, Blue accent #3182CE
1665    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1666    doc.set_margins(
1667        Length::inches(1.25),
1668        Length::inches(1.25),
1669        Length::inches(1.25),
1670        Length::inches(1.25),
1671    );
1672    doc.set_title("Business Letter");
1673    doc.set_author("Walter White");
1674
1675    // Banner header using raw XML
1676    let logo_img = create_logo_png(220, 48);
1677    let banner = build_header_banner_xml(
1678        "rId1",
1679        &BannerOpts {
1680            bg_color: "4A5568",
1681            banner_width: 7772400,
1682            banner_height: 731520, // ~0.8"
1683            logo_width: 2011680,
1684            logo_height: 438912,
1685            logo_x_offset: 295125,
1686            logo_y_offset: 146304,
1687        },
1688    );
1689    doc.set_raw_header_with_images(
1690        banner,
1691        &[("rId1", &logo_img, "logo.png")],
1692        rdocx_oxml::header_footer::HdrFtrType::Default,
1693    );
1694    doc.set_margins(
1695        Length::twips(2000),
1696        Length::twips(1800),
1697        Length::twips(1440),
1698        Length::twips(1800),
1699    );
1700    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
1701
1702    // Footer with contact
1703    doc.set_footer(
1704        "Tensorbee | 123 Innovation Drive, Suite 400 | San Francisco, CA 94105 | tensorbee.com",
1705    );
1706
1707    // ── Sender Address ──
1708    doc.add_paragraph("Walter White");
1709    doc.add_paragraph("Chief Executive Officer");
1710    doc.add_paragraph("Tensorbee");
1711    doc.add_paragraph("123 Innovation Drive, Suite 400");
1712    doc.add_paragraph("San Francisco, CA 94105");
1713    doc.add_paragraph("");
1714    doc.add_paragraph("February 22, 2026");
1715
1716    doc.add_paragraph("");
1717
1718    // ── Recipient ──
1719    doc.add_paragraph("Ms. Sarah Chen");
1720    doc.add_paragraph("VP of Engineering");
1721    doc.add_paragraph("Meridian Dynamics LLC");
1722    doc.add_paragraph("456 Enterprise Boulevard");
1723    doc.add_paragraph("Austin, TX 78701");
1724
1725    doc.add_paragraph("");
1726
1727    // ── Subject ──
1728    {
1729        let mut p = doc.add_paragraph("");
1730        p.add_run("Re: Strategic Technology Partnership — Phase 2 Expansion")
1731            .bold(true);
1732    }
1733
1734    doc.add_paragraph("");
1735
1736    // ── Body ──
1737    doc.add_paragraph("Dear Ms. Chen,");
1738    doc.add_paragraph("");
1739
1740    doc.add_paragraph(
1741        "I am writing to express Tensorbee's enthusiasm for expanding our strategic technology \
1742         partnership with Meridian Dynamics. Following the successful completion of Phase 1, \
1743         which delivered a 40% improvement in your ML inference pipeline throughput, we believe \
1744         the foundation is firmly established for an ambitious Phase 2 engagement.",
1745    )
1746    .first_line_indent(Length::inches(0.5));
1747
1748    doc.add_paragraph(
1749        "During our recent executive review, your team highlighted three priority areas for \
1750         the next phase of collaboration:",
1751    )
1752    .first_line_indent(Length::inches(0.5));
1753
1754    doc.add_numbered_list_item(
1755        "Real-time anomaly detection for your financial transaction monitoring system, \
1756         targeting sub-100ms latency at 50,000 transactions per second.",
1757        0,
1758    );
1759    doc.add_numbered_list_item(
1760        "Federated learning infrastructure to enable model training across your distributed \
1761         data centers without centralizing sensitive financial data.",
1762        0,
1763    );
1764    doc.add_numbered_list_item(
1765        "MLOps automation to reduce your model deployment cycle from the current 5 days \
1766         to under 4 hours.",
1767        0,
1768    );
1769
1770    doc.add_paragraph(
1771        "Our engineering team has prepared a detailed technical proposal addressing each of \
1772         these areas. We have allocated a dedicated team of eight senior engineers, led by \
1773         our CTO, to ensure continuity with the Phase 1 team your organization has already \
1774         built a productive working relationship with.",
1775    )
1776    .first_line_indent(Length::inches(0.5));
1777
1778    doc.add_paragraph(
1779        "I would welcome the opportunity to present our Phase 2 proposal in person. My \
1780         assistant will reach out to coordinate a meeting at your convenience during the \
1781         first week of March.",
1782    )
1783    .first_line_indent(Length::inches(0.5));
1784
1785    doc.add_paragraph("");
1786
1787    doc.add_paragraph("Warm regards,");
1788    doc.add_paragraph("");
1789    doc.add_paragraph("");
1790
1791    // Signature line
1792    doc.add_paragraph("")
1793        .border_bottom(BorderStyle::Single, 4, "4A5568");
1794    {
1795        let mut p = doc.add_paragraph("");
1796        p.add_run("Walter White").bold(true).size(12.0);
1797    }
1798    {
1799        let mut p = doc.add_paragraph("");
1800        p.add_run("Chief Executive Officer, Tensorbee")
1801            .italic(true)
1802            .size(10.0)
1803            .color("666666");
1804    }
1805    {
1806        let mut p = doc.add_paragraph("");
1807        p.add_run("walter@tensorbee.com | +1 (415) 555-0199")
1808            .size(10.0)
1809            .color("888888");
1810    }
1811
1812    doc
1813}
Source

pub fn set_first_line_indent(&mut self, length: Length)

Set first line indent in place.

Source

pub fn set_signed_first_line_indent_value(&mut self, length: Option<Length>)

Set or clear the signed first-line indentation.

Negative values are stored as a positive hanging indentation.

Source

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

Set hanging indent.

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

pub fn set_hanging_indent(&mut self, length: Length)

Set hanging indent in place.

Source

pub fn keep_with_next(self, val: bool) -> Self

Set keep with next paragraph.

Examples found in repository?
examples/generate_all_samples.rs (line 301)
97fn generate_feature_showcase(_samples_dir: &Path) -> Document {
98    let mut doc = Document::new();
99
100    // ── Page Setup & Metadata ──
101    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
102    doc.set_margins(
103        Length::inches(1.0),
104        Length::inches(1.0),
105        Length::inches(1.0),
106        Length::inches(1.0),
107    );
108    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
109    doc.set_gutter(Length::twips(0));
110    doc.set_title("rdocx Feature Showcase");
111    doc.set_author("rdocx Sample Generator");
112    doc.set_subject("Comprehensive feature demonstration");
113    doc.set_keywords("rdocx, docx, rust, sample, showcase");
114
115    // Headers & Footers with different first page
116    doc.set_different_first_page(true);
117    doc.set_first_page_header("rdocx");
118    doc.set_first_page_footer("Feature Showcase — Cover Page");
119    doc.set_header("rdocx Feature Showcase");
120    doc.set_footer("Generated by rdocx");
121
122    // ── COVER PAGE ──
123    let bg = create_sample_png(612, 792, [20, 45, 90]);
124    doc.add_background_image(&bg, "cover_bg.png");
125
126    for _ in 0..3 {
127        doc.add_paragraph("");
128    }
129    {
130        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
131        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
132    }
133    {
134        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
135        p.add_run("Complete Feature Showcase")
136            .size(28.0)
137            .color("FFFFFF")
138            .italic(true);
139    }
140    doc.add_paragraph("");
141    {
142        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
143        p.add_run("Every feature of the rdocx Rust library")
144            .size(13.0)
145            .color("B0C4DE");
146    }
147    {
148        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
149        p.add_run("demonstrated in a single document.")
150            .size(13.0)
151            .color("B0C4DE");
152    }
153
154    // ── TABLE OF CONTENTS ──
155    doc.add_paragraph("").page_break_before(true);
156    doc.insert_toc(doc.content_count(), 3);
157
158    // ── SECTION 1: TEXT FORMATTING ──
159    doc.add_paragraph("").page_break_before(true);
160    doc.add_paragraph("1. Text Formatting").style("Heading1");
161
162    // Paragraph Alignment
163    doc.add_paragraph("1.1 Paragraph Alignment")
164        .style("Heading2");
165    doc.add_paragraph("Left-aligned paragraph (default).")
166        .alignment(Alignment::Left);
167    doc.add_paragraph("Center-aligned paragraph.")
168        .alignment(Alignment::Center);
169    doc.add_paragraph("Right-aligned paragraph.")
170        .alignment(Alignment::Right);
171    doc.add_paragraph(
172        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
173         distributes extra space across word gaps so lines fill the full width of the text area.",
174    )
175    .alignment(Alignment::Justify);
176
177    doc.add_paragraph("");
178
179    // Run Formatting
180    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
181    {
182        let mut p = doc.add_paragraph("");
183        p.add_run("Bold").bold(true);
184        p.add_run(" | ");
185        p.add_run("Italic").italic(true);
186        p.add_run(" | ");
187        p.add_run("Bold Italic").bold(true).italic(true);
188        p.add_run(" | ");
189        p.add_run("Underline").underline(true);
190        p.add_run(" | ");
191        p.add_run("Strikethrough").strike(true);
192        p.add_run(" | ");
193        p.add_run("Double Strike").double_strike(true);
194    }
195    {
196        let mut p = doc.add_paragraph("");
197        p.add_run("Red").color("FF0000");
198        p.add_run(" | ");
199        p.add_run("Blue").color("0000FF");
200        p.add_run(" | ");
201        p.add_run("Green").color("00AA00");
202        p.add_run(" | ");
203        p.add_run("Highlighted").highlight("FFFF00");
204    }
205    {
206        let mut p = doc.add_paragraph("");
207        p.add_run("8pt").size(8.0);
208        p.add_run(" | ");
209        p.add_run("11pt").size(11.0);
210        p.add_run(" | ");
211        p.add_run("16pt").size(16.0);
212        p.add_run(" | ");
213        p.add_run("24pt").size(24.0);
214    }
215    {
216        let mut p = doc.add_paragraph("");
217        p.add_run("Arial").font("Arial");
218        p.add_run(" | ");
219        p.add_run("Times New Roman").font("Times New Roman");
220        p.add_run(" | ");
221        p.add_run("Courier New").font("Courier New");
222    }
223    {
224        let mut p = doc.add_paragraph("");
225        p.add_run("H");
226        p.add_run("2").subscript();
227        p.add_run("O (subscript) | E=mc");
228        p.add_run("2").superscript();
229        p.add_run(" (superscript)");
230    }
231    {
232        let mut p = doc.add_paragraph("");
233        p.add_run("ALL CAPS").all_caps(true);
234        p.add_run(" | ");
235        p.add_run("Small Caps").small_caps(true);
236        p.add_run(" | ");
237        p.add_run("Expanded").character_spacing(Length::twips(40));
238        p.add_run(" | ");
239        p.add_run("Hidden text (not visible)").hidden(true);
240    }
241
242    doc.add_paragraph("");
243
244    // Underline Styles
245    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
246    {
247        let mut p = doc.add_paragraph("");
248        p.add_run("Single ")
249            .underline_style(rdocx::UnderlineStyle::Single);
250        p.add_run("Double ")
251            .underline_style(rdocx::UnderlineStyle::Double);
252        p.add_run("Thick ")
253            .underline_style(rdocx::UnderlineStyle::Thick);
254        p.add_run("Dotted ")
255            .underline_style(rdocx::UnderlineStyle::Dotted);
256        p.add_run("Dash ")
257            .underline_style(rdocx::UnderlineStyle::Dash);
258        p.add_run("Wave ")
259            .underline_style(rdocx::UnderlineStyle::Wave);
260    }
261
262    // ── SECTION 2: PARAGRAPH FORMATTING ──
263    doc.add_paragraph("").page_break_before(true);
264    doc.add_paragraph("2. Paragraph Formatting")
265        .style("Heading1");
266
267    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
268    doc.add_paragraph("Paragraph with light green shading")
269        .shading("E2EFDA");
270    doc.add_paragraph("Paragraph with bottom border")
271        .border_bottom(BorderStyle::Single, 6, "2E75B6");
272    doc.add_paragraph("Paragraph with all borders (red)")
273        .border_all(BorderStyle::Single, 4, "FF0000");
274
275    doc.add_paragraph("2.2 Indentation").style("Heading2");
276    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
277        .indent_left(Length::inches(1.0))
278        .hanging_indent(Length::inches(0.5));
279    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
280        .first_line_indent(Length::inches(0.5));
281    doc.add_paragraph("Both left and right indent (0.75 inches each)")
282        .indent_left(Length::inches(0.75))
283        .indent_right(Length::inches(0.75));
284
285    doc.add_paragraph("2.3 Spacing & Line Height")
286        .style("Heading2");
287    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
288        .space_before(Length::pt(24.0))
289        .space_after(Length::pt(12.0));
290    doc.add_paragraph(
291        "Double line spacing paragraph. This text should have extra vertical space between \
292         lines to demonstrate line_spacing_multiple(2.0).",
293    )
294    .line_spacing_multiple(2.0);
295    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
296        .line_spacing(20.0);
297
298    doc.add_paragraph("2.4 Pagination Controls")
299        .style("Heading2");
300    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
301        .keep_with_next(true);
302    doc.add_paragraph("(This paragraph was kept with the one above.)");
303    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
304        .keep_together(true);
305    doc.add_paragraph("widow_control — prevents widow/orphan lines")
306        .widow_control(true);
307
308    // ── SECTION 3: LISTS ──
309    doc.add_paragraph("").page_break_before(true);
310    doc.add_paragraph("3. Lists").style("Heading1");
311
312    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
313    doc.add_bullet_list_item("First item", 0);
314    doc.add_bullet_list_item("Second item", 0);
315    doc.add_bullet_list_item("Nested level 1", 1);
316    doc.add_bullet_list_item("Nested level 2", 2);
317    doc.add_bullet_list_item("Back to level 1", 1);
318    doc.add_bullet_list_item("Third item", 0);
319
320    doc.add_paragraph("");
321
322    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
323    doc.add_numbered_list_item("First numbered item", 0);
324    doc.add_numbered_list_item("Second numbered item", 0);
325    doc.add_numbered_list_item("Sub-item A", 1);
326    doc.add_numbered_list_item("Sub-item B", 1);
327    doc.add_numbered_list_item("Third numbered item", 0);
328
329    // ── SECTION 4: TAB STOPS ──
330    doc.add_paragraph("");
331    doc.add_paragraph("4. Tab Stops").style("Heading1");
332
333    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
334    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
335        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
336        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
337        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
338        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
339
340    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
341    doc.add_paragraph("Item\t\tPrice")
342        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
343        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
344        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
345    doc.add_paragraph("Widget\t\t$19.99")
346        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
347        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
348        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
349    doc.add_paragraph("Gadget\t\t$249.50")
350        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
351        .add_tab_stop_with_leader(
352            TabAlignment::Right,
353            Length::inches(4.0),
354            TabLeader::Underscore,
355        )
356        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
357
358    // ── SECTION 5: TABLES ──
359    doc.add_paragraph("").page_break_before(true);
360    doc.add_paragraph("5. Tables").style("Heading1");
361
362    doc.add_paragraph("5.1 Basic Table").style("Heading2");
363    {
364        let mut tbl = doc
365            .add_table(4, 3)
366            .borders(BorderStyle::Single, 4, "000000");
367        for col in 0..3 {
368            tbl.cell(0, col).unwrap().shading("2E75B6");
369        }
370        tbl.cell(0, 0).unwrap().set_text("Name");
371        tbl.cell(0, 1).unwrap().set_text("Role");
372        tbl.cell(0, 2).unwrap().set_text("Location");
373        tbl.cell(1, 0).unwrap().set_text("Walter White");
374        tbl.cell(1, 1).unwrap().set_text("CEO");
375        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
376        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
377        tbl.cell(2, 1).unwrap().set_text("CTO");
378        tbl.cell(2, 2).unwrap().set_text("Remote");
379        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
380        tbl.cell(3, 1).unwrap().set_text("Security");
381        tbl.cell(3, 2).unwrap().set_text("Washington");
382    }
383
384    doc.add_paragraph("");
385
386    doc.add_paragraph("5.2 Column Span & Cell Shading")
387        .style("Heading2");
388    {
389        let mut tbl = doc
390            .add_table(3, 4)
391            .borders(BorderStyle::Single, 4, "000000")
392            .width_pct(100.0);
393        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
394        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
395        tbl.cell(1, 0).unwrap().set_text("Region");
396        tbl.cell(1, 0).unwrap().shading("D6E4F0");
397        tbl.cell(1, 1).unwrap().set_text("Q1");
398        tbl.cell(1, 1).unwrap().shading("D6E4F0");
399        tbl.cell(1, 2).unwrap().set_text("Q2");
400        tbl.cell(1, 2).unwrap().shading("D6E4F0");
401        tbl.cell(1, 3).unwrap().set_text("Total");
402        tbl.cell(1, 3).unwrap().shading("D6E4F0");
403        tbl.cell(2, 0).unwrap().set_text("Americas");
404        tbl.cell(2, 1).unwrap().set_text("$2.4M");
405        tbl.cell(2, 2).unwrap().set_text("$2.7M");
406        tbl.cell(2, 3).unwrap().set_text("$5.1M");
407    }
408
409    doc.add_paragraph("");
410
411    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
412    {
413        let mut tbl = doc
414            .add_table(4, 3)
415            .borders(BorderStyle::Single, 4, "000000");
416        tbl.cell(0, 0).unwrap().set_text("Category");
417        tbl.cell(0, 0).unwrap().shading("E2EFDA");
418        tbl.cell(0, 1).unwrap().set_text("Item");
419        tbl.cell(0, 1).unwrap().shading("E2EFDA");
420        tbl.cell(0, 2).unwrap().set_text("Price");
421        tbl.cell(0, 2).unwrap().shading("E2EFDA");
422        tbl.cell(1, 0).unwrap().set_text("Hardware");
423        tbl.cell(1, 0).unwrap().v_merge_restart();
424        tbl.cell(1, 1).unwrap().set_text("Laptop");
425        tbl.cell(1, 2).unwrap().set_text("$1,200");
426        tbl.cell(2, 0).unwrap().v_merge_continue();
427        tbl.cell(2, 1).unwrap().set_text("Monitor");
428        tbl.cell(2, 2).unwrap().set_text("$450");
429        tbl.cell(3, 0).unwrap().set_text("Software");
430        tbl.cell(3, 1).unwrap().set_text("IDE License");
431        tbl.cell(3, 2).unwrap().set_text("$200/yr");
432    }
433
434    doc.add_paragraph("");
435
436    doc.add_paragraph("5.4 Nested Table").style("Heading2");
437    {
438        let mut tbl = doc
439            .add_table(2, 2)
440            .borders(BorderStyle::Single, 6, "2E75B6");
441        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
442        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
443        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
444        {
445            let mut cell = tbl.cell(1, 1).unwrap();
446            cell.set_text("Nested table below:");
447            let mut nested = cell
448                .add_table(2, 2)
449                .borders(BorderStyle::Single, 2, "FF6600");
450            nested.cell(0, 0).unwrap().set_text("A");
451            nested.cell(0, 1).unwrap().set_text("B");
452            nested.cell(1, 0).unwrap().set_text("C");
453            nested.cell(1, 1).unwrap().set_text("D");
454        }
455    }
456
457    doc.add_paragraph("");
458
459    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
460        .style("Heading2");
461    {
462        let mut tbl = doc
463            .add_table(2, 3)
464            .borders(BorderStyle::Single, 4, "666666");
465        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
466        tbl.cell(0, 0).unwrap().set_text("Top");
467        tbl.cell(0, 0)
468            .unwrap()
469            .vertical_alignment(VerticalAlignment::Top)
470            .shading("FFF2CC");
471        tbl.cell(0, 1).unwrap().set_text("Center");
472        tbl.cell(0, 1)
473            .unwrap()
474            .vertical_alignment(VerticalAlignment::Center)
475            .shading("D9E2F3");
476        tbl.cell(0, 2).unwrap().set_text("Bottom");
477        tbl.cell(0, 2)
478            .unwrap()
479            .vertical_alignment(VerticalAlignment::Bottom)
480            .shading("E2EFDA");
481        tbl.row(1).unwrap().cant_split();
482        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
483        tbl.cell(1, 0).unwrap().no_wrap();
484        tbl.cell(1, 1).unwrap().set_text("Fixed width");
485        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
486        tbl.cell(1, 2).unwrap().set_text("Normal");
487    }
488
489    // ── SECTION 6: IMAGES ──
490    doc.add_paragraph("").page_break_before(true);
491    doc.add_paragraph("6. Images").style("Heading1");
492
493    doc.add_paragraph("6.1 Inline Image").style("Heading2");
494    doc.add_paragraph("A blue gradient image below:");
495    let img = create_sample_png(200, 50, [0, 80, 200]);
496    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
497
498    doc.add_paragraph("");
499    doc.add_paragraph("6.2 Header Image").style("Heading2");
500    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
501    doc.set_header_image(
502        &hdr_img,
503        "header_logo.png",
504        Length::inches(2.0),
505        Length::inches(0.2),
506    );
507    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
508
509    doc.add_paragraph("");
510    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
511
512    // ── SECTION 7: CONTENT MANIPULATION ──
513    doc.add_paragraph("").page_break_before(true);
514    doc.add_paragraph("7. Content Manipulation")
515        .style("Heading1");
516
517    doc.add_paragraph("7.1 Placeholder Replacement")
518        .style("Heading2");
519    doc.add_paragraph("Customer: {{customer}}");
520    doc.add_paragraph("Date: {{date}}");
521    doc.add_paragraph("Reference: {{ref_number}}");
522    {
523        let mut tbl = doc
524            .add_table(2, 2)
525            .borders(BorderStyle::Single, 4, "000000");
526        tbl.cell(0, 0).unwrap().set_text("Project");
527        tbl.cell(0, 0).unwrap().shading("D6E4F0");
528        tbl.cell(0, 1).unwrap().set_text("{{project}}");
529        tbl.cell(1, 0).unwrap().set_text("Status");
530        tbl.cell(1, 0).unwrap().shading("D6E4F0");
531        tbl.cell(1, 1).unwrap().set_text("{{status}}");
532    }
533
534    let mut replacements = HashMap::new();
535    replacements.insert("{{customer}}", "Tensorbee Inc.");
536    replacements.insert("{{date}}", "February 22, 2026");
537    replacements.insert("{{ref_number}}", "TB-2026-001");
538    replacements.insert("{{project}}", "Infrastructure Upgrade");
539    replacements.insert("{{status}}", "In Progress");
540    let n = doc.replace_all(&replacements);
541    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
542
543    doc.add_paragraph("");
544
545    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
546    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
547    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
548    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
549
550    doc.add_paragraph("");
551
552    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
553    doc.add_paragraph("Section A: First content.");
554    doc.add_paragraph("Section C: Third content.");
555    if let Some(idx) = doc.find_content_index("Section C") {
556        doc.insert_paragraph(
557            idx,
558            "Section B: Inserted between A and C via find_content_index().",
559        );
560    }
561
562    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
563    doc.add_paragraph("").section_break(SectionBreak::NextPage);
564    doc.add_paragraph("8. Section Breaks & Orientation")
565        .style("Heading1");
566    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
567
568    {
569        let mut tbl = doc
570            .add_table(3, 7)
571            .borders(BorderStyle::Single, 4, "2E75B6");
572        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
573        for (c, h) in headers.iter().enumerate() {
574            tbl.cell(0, c).unwrap().set_text(h);
575            tbl.cell(0, c).unwrap().shading("2E75B6");
576        }
577        let data = [
578            [
579                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
580            ],
581            [
582                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
583            ],
584        ];
585        for (r, row) in data.iter().enumerate() {
586            for (c, v) in row.iter().enumerate() {
587                tbl.cell(r + 1, c).unwrap().set_text(v);
588            }
589        }
590    }
591
592    doc.add_paragraph("")
593        .section_break(SectionBreak::NextPage)
594        .section_landscape();
595
596    // ── SECTION 9: CUSTOM STYLES ──
597    doc.add_paragraph("9. Custom Styles").style("Heading1");
598    doc.add_style(
599        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
600            .based_on("Normal")
601            .paragraph_properties(rdocx_oxml::properties::CT_PPr {
602                shading: Some(rdocx_oxml::properties::CT_Shd {
603                    val: "clear".to_string(),
604                    color: None,
605                    fill: Some("FFF2CC".to_string()),
606                }),
607                ..Default::default()
608            })
609            .run_properties(rdocx_oxml::properties::CT_RPr {
610                bold: Some(true),
611                color: Some("C45911".to_string()),
612                ..Default::default()
613            }),
614    );
615    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
616        .style("CustomHighlight");
617
618    doc.add_paragraph("");
619
620    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
621    doc.add_paragraph("10. Document Intelligence API")
622        .style("Heading1");
623
624    let wc = doc.word_count();
625    let hc = doc.headings().len();
626    let ic = doc.images().len();
627    let lc = doc.links().len();
628    doc.add_paragraph(&format!("Word count: {wc}"));
629    doc.add_paragraph(&format!("Heading count: {hc}"));
630    doc.add_paragraph(&format!("Image count: {ic}"));
631    doc.add_paragraph(&format!("Link count: {lc}"));
632
633    let outline = doc.document_outline();
634    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
635
636    let issues = doc.audit_accessibility();
637    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
638    for issue in &issues {
639        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
640    }
641
642    // ── SECTION 11: DOCUMENT MERGING ──
643    doc.add_paragraph("");
644    doc.add_paragraph("11. Document Merging").style("Heading1");
645    {
646        let mut other = Document::new();
647        other.add_paragraph("This paragraph was merged from another document using append().");
648        doc.append(&other);
649    }
650
651    doc.add_paragraph("");
652
653    // ── SUMMARY ──
654    doc.add_paragraph("Summary of Demonstrated Features")
655        .style("Heading1");
656    let features = [
657        "Page setup: size, margins, header/footer distance, gutter",
658        "Document metadata: title, author, subject, keywords",
659        "Headers and footers: text, images, different first page",
660        "Background images (full-page behind text)",
661        "Table of Contents generation with bookmarks",
662        "Text formatting: bold, italic, underline styles, strike, color, size, font",
663        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
664        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
665        "Pagination controls: keep-with-next, keep-together, widow control",
666        "Bullet and numbered lists with nesting",
667        "Tab stops with dot/underscore/hyphen leaders",
668        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
669        "Row properties: header rows, exact height, cant-split",
670        "Cell properties: width, no-wrap, vertical alignment",
671        "Inline images and header images",
672        "Placeholder replacement (single and batch)",
673        "Regex-based find and replace",
674        "Content insertion at specific positions",
675        "Section breaks with mixed portrait/landscape",
676        "Custom paragraph and character styles",
677        "Document intelligence: word count, headings, outline, images, links",
678        "Accessibility audit",
679        "Document merging (append)",
680        "Export: DOCX, PDF, HTML, Markdown",
681    ];
682    for f in &features {
683        doc.add_bullet_list_item(f, 0);
684    }
685    doc.add_paragraph("");
686    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
687        .alignment(Alignment::Center)
688        .shading("E2EFDA")
689        .border_all(BorderStyle::Single, 2, "00AA00");
690
691    doc
692}
693
694// =============================================================================
695// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
696// =============================================================================
697fn generate_proposal(_samples_dir: &Path) -> Document {
698    let mut doc = Document::new();
699
700    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
701    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
702    doc.set_margins(
703        Length::inches(1.0),
704        Length::inches(1.0),
705        Length::inches(1.0),
706        Length::inches(1.0),
707    );
708    doc.set_title("AI Platform Modernization Proposal");
709    doc.set_author("Walter White");
710    doc.set_subject("Technology Proposal");
711    doc.set_keywords("proposal, AI, modernization, Tensorbee");
712
713    // Banner header
714    let logo_img = create_logo_png(220, 48);
715    let banner = build_header_banner_xml(
716        "rId1",
717        &BannerOpts {
718            bg_color: "1B2A4A",
719            banner_width: 7772400,
720            banner_height: 914400,
721            logo_width: 2011680,
722            logo_height: 438912,
723            logo_x_offset: 295125,
724            logo_y_offset: 237744,
725        },
726    );
727    doc.set_raw_header_with_images(
728        banner,
729        &[("rId1", &logo_img, "logo.png")],
730        rdocx_oxml::header_footer::HdrFtrType::Default,
731    );
732    doc.set_different_first_page(true);
733    let cover_banner = build_header_banner_xml(
734        "rId1",
735        &BannerOpts {
736            bg_color: "C5922E",
737            banner_width: 7772400,
738            banner_height: 914400,
739            logo_width: 2011680,
740            logo_height: 438912,
741            logo_x_offset: 295125,
742            logo_y_offset: 237744,
743        },
744    );
745    doc.set_raw_header_with_images(
746        cover_banner,
747        &[("rId1", &logo_img, "logo.png")],
748        rdocx_oxml::header_footer::HdrFtrType::First,
749    );
750    doc.set_margins(
751        Length::twips(2292),
752        Length::twips(1440),
753        Length::twips(1440),
754        Length::twips(1440),
755    );
756    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
757    doc.set_footer("Tensorbee — Confidential");
758
759    // Custom styles
760    doc.add_style(
761        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
762            .based_on("Normal")
763            .run_properties(rdocx_oxml::properties::CT_RPr {
764                bold: Some(true),
765                font_ascii: Some("Georgia".to_string()),
766                font_hansi: Some("Georgia".to_string()),
767                sz: Some(rdocx_oxml::HalfPoint(56)), // half-points → 28pt
768                color: Some("1B2A4A".to_string()),
769                ..Default::default()
770            }),
771    );
772
773    // ── Cover Page ──
774    for _ in 0..4 {
775        doc.add_paragraph("");
776    }
777    doc.add_paragraph("AI Platform Modernization")
778        .style("ProposalTitle")
779        .alignment(Alignment::Center);
780    doc.add_paragraph("")
781        .alignment(Alignment::Center)
782        .border_bottom(BorderStyle::Single, 8, "C5922E");
783    {
784        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
785        p.add_run("Prepared for Global Financial Corp.")
786            .size(14.0)
787            .color("666666")
788            .italic(true);
789    }
790    {
791        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
792        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
793            .size(12.0)
794            .color("888888");
795    }
796    {
797        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
798        p.add_run("February 22, 2026").size(12.0).color("888888");
799    }
800
801    // ── TOC ──
802    doc.add_paragraph("").page_break_before(true);
803    doc.add_paragraph("Table of Contents").style("Heading1");
804    // We'll add a manual-style TOC since the insert_toc goes at a specific position
805    doc.insert_toc(doc.content_count(), 2);
806
807    // ── Executive Summary ──
808    doc.add_paragraph("").page_break_before(true);
809    doc.add_paragraph("Executive Summary").style("Heading1");
810    doc.add_paragraph(
811        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
812         transitioning from legacy batch-processing systems to a real-time inference architecture. \
813         This transformation will reduce model deployment time from weeks to hours, improve \
814         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
815    )
816    .first_line_indent(Length::inches(0.3));
817    doc.add_paragraph(
818        "The project spans three phases over 18 months, with a total investment of $2.8M. \
819         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
820         similar transformations for three Fortune 500 financial institutions.",
821    )
822    .first_line_indent(Length::inches(0.3));
823
824    // Key metrics table
825    doc.add_paragraph("");
826    {
827        let mut tbl = doc
828            .add_table(5, 2)
829            .borders(BorderStyle::Single, 4, "1B2A4A")
830            .width_pct(80.0);
831        tbl.cell(0, 0).unwrap().set_text("Key Metric");
832        tbl.cell(0, 0).unwrap().shading("1B2A4A");
833        tbl.cell(0, 1).unwrap().set_text("Value");
834        tbl.cell(0, 1).unwrap().shading("1B2A4A");
835        let rows = [
836            ("Total Investment", "$2.8M"),
837            ("Annual Savings", "$4.2M"),
838            ("ROI (Year 1)", "150%"),
839            ("Timeline", "18 months"),
840        ];
841        for (i, (k, v)) in rows.iter().enumerate() {
842            tbl.cell(i + 1, 0).unwrap().set_text(k);
843            if i % 2 == 0 {
844                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
845            }
846            tbl.cell(i + 1, 1).unwrap().set_text(v);
847            if i % 2 == 0 {
848                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
849            }
850        }
851    }
852
853    // ── Problem Statement ──
854    doc.add_paragraph("").page_break_before(true);
855    doc.add_paragraph("Problem Statement").style("Heading1");
856    doc.add_paragraph(
857        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
858    );
859    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);
860    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
861    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
862
863    // ── Proposed Solution ──
864    doc.add_paragraph("").page_break_before(true);
865    doc.add_paragraph("Proposed Solution").style("Heading1");
866
867    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
868        .style("Heading2");
869    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
870    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
871    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
872
873    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
874        .style("Heading2");
875    doc.add_bullet_list_item(
876        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
877        0,
878    );
879    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
880    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
881
882    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
883        .style("Heading2");
884    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
885    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
886    doc.add_bullet_list_item(
887        "Self-service model deployment portal for data science teams",
888        0,
889    );
890
891    // ── Budget ──
892    doc.add_paragraph("Budget Breakdown").style("Heading1");
893    {
894        let mut tbl = doc
895            .add_table(6, 3)
896            .borders(BorderStyle::Single, 4, "1B2A4A")
897            .width_pct(100.0);
898        tbl.cell(0, 0).unwrap().set_text("Category");
899        tbl.cell(0, 0).unwrap().shading("1B2A4A");
900        tbl.cell(0, 1).unwrap().set_text("Cost");
901        tbl.cell(0, 1).unwrap().shading("1B2A4A");
902        tbl.cell(0, 2).unwrap().set_text("Notes");
903        tbl.cell(0, 2).unwrap().shading("1B2A4A");
904        let items = [
905            (
906                "Infrastructure",
907                "$450,000",
908                "Cloud compute + GPU instances",
909            ),
910            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
911            (
912                "Software Licenses",
913                "$350,000",
914                "Kafka, monitoring, CI/CD tools",
915            ),
916            (
917                "Training & Enablement",
918                "$200,000",
919                "Team training, documentation",
920            ),
921            ("Contingency (10%)", "$200,000", "Risk buffer"),
922        ];
923        for (i, (cat, cost, note)) in items.iter().enumerate() {
924            tbl.cell(i + 1, 0).unwrap().set_text(cat);
925            tbl.cell(i + 1, 1).unwrap().set_text(cost);
926            tbl.cell(i + 1, 2).unwrap().set_text(note);
927            if i % 2 == 0 {
928                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
929                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
930                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
931            }
932        }
933    }
934
935    // ── Closing ──
936    doc.add_paragraph("");
937    doc.add_paragraph("Next Steps").style("Heading1");
938    doc.add_numbered_list_item(
939        "Schedule technical deep-dive with GFC engineering team (Week 1)",
940        0,
941    );
942    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
943    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
944
945    doc.add_paragraph("");
946    {
947        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
948        p.add_run("Walter White")
949            .bold(true)
950            .size(14.0)
951            .color("1B2A4A");
952    }
953    {
954        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
955        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
956            .size(10.0)
957            .color("888888");
958    }
959
960    doc
961}
962
963// =============================================================================
964// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
965// =============================================================================
966fn generate_quote(_samples_dir: &Path) -> Document {
967    let mut doc = Document::new();
968
969    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
970    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
971    doc.set_margins(
972        Length::inches(0.75),
973        Length::inches(0.75),
974        Length::inches(0.75),
975        Length::inches(0.75),
976    );
977    doc.set_title("Quotation QT-2026-0147");
978    doc.set_author("Walter White");
979    doc.set_keywords("quote, BOM, Tensorbee");
980
981    doc.set_header("Tensorbee — Quotation");
982    doc.set_footer("QT-2026-0147 | Page");
983
984    // ── Header Block ──
985    {
986        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
987        p.add_run("TENSORBEE")
988            .bold(true)
989            .size(28.0)
990            .color("008B8B")
991            .font("Helvetica");
992    }
993    doc.add_paragraph("123 Innovation Drive, Suite 400")
994        .alignment(Alignment::Left);
995    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
996        .alignment(Alignment::Left);
997    doc.add_paragraph("")
998        .border_bottom(BorderStyle::Single, 8, "008B8B");
999
1000    // Quote meta
1001    doc.add_paragraph("");
1002    {
1003        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
1004        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
1005        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
1006        tbl.cell(0, 2).unwrap().set_text("DATE");
1007        tbl.cell(0, 2).unwrap().shading("008B8B");
1008        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1009        tbl.cell(0, 3).unwrap().shading("008B8B");
1010        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1011        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1012        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1013        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1014        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1015        tbl.cell(2, 1).unwrap().set_text("Walter White");
1016        tbl.cell(2, 2).unwrap().set_text("Payment:");
1017        tbl.cell(2, 3).unwrap().set_text("Net 30");
1018        tbl.cell(3, 0).unwrap().set_text("Customer:");
1019        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1020        tbl.cell(3, 2).unwrap().set_text("Currency:");
1021        tbl.cell(3, 3).unwrap().set_text("USD");
1022    }
1023
1024    doc.add_paragraph("");
1025
1026    // ── Bill of Materials ──
1027    {
1028        let mut p = doc.add_paragraph("");
1029        p.add_run("Bill of Materials")
1030            .bold(true)
1031            .size(16.0)
1032            .color("1A3C3C");
1033    }
1034    doc.add_paragraph("");
1035
1036    {
1037        let mut tbl = doc
1038            .add_table(10, 6)
1039            .borders(BorderStyle::Single, 2, "008B8B")
1040            .width_pct(100.0)
1041            .layout_fixed();
1042        // Headers
1043        let hdrs = [
1044            "#",
1045            "Part Number",
1046            "Description",
1047            "Qty",
1048            "Unit Price",
1049            "Total",
1050        ];
1051        for (c, h) in hdrs.iter().enumerate() {
1052            tbl.cell(0, c).unwrap().set_text(h);
1053            tbl.cell(0, c).unwrap().shading("008B8B");
1054        }
1055
1056        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1057            (
1058                "TB-GPU-A100",
1059                "NVIDIA A100 80GB GPU",
1060                "4",
1061                "$12,500.00",
1062                "$50,000.00",
1063            ),
1064            (
1065                "TB-SRV-R750",
1066                "Dell R750xa Server Chassis",
1067                "2",
1068                "$8,200.00",
1069                "$16,400.00",
1070            ),
1071            (
1072                "TB-RAM-256G",
1073                "256GB DDR5 ECC Memory Module",
1074                "8",
1075                "$890.00",
1076                "$7,120.00",
1077            ),
1078            (
1079                "TB-SSD-3840",
1080                "3.84TB NVMe U.2 SSD",
1081                "8",
1082                "$1,150.00",
1083                "$9,200.00",
1084            ),
1085            (
1086                "TB-NET-CX7",
1087                "ConnectX-7 200GbE NIC",
1088                "4",
1089                "$1,800.00",
1090                "$7,200.00",
1091            ),
1092            (
1093                "TB-SW-48P",
1094                "48-Port 100GbE Switch",
1095                "1",
1096                "$22,000.00",
1097                "$22,000.00",
1098            ),
1099            (
1100                "TB-CAB-RACK",
1101                "42U Server Rack + PDU",
1102                "1",
1103                "$4,500.00",
1104                "$4,500.00",
1105            ),
1106            (
1107                "TB-SVC-INST",
1108                "Installation & Configuration",
1109                "1",
1110                "$8,500.00",
1111                "$8,500.00",
1112            ),
1113            (
1114                "TB-SVC-SUPP",
1115                "3-Year Premium Support",
1116                "1",
1117                "$15,000.00",
1118                "$15,000.00",
1119            ),
1120        ];
1121
1122        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1123            let row = i + 1;
1124            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1125            tbl.cell(row, 1).unwrap().set_text(pn);
1126            tbl.cell(row, 2).unwrap().set_text(desc);
1127            tbl.cell(row, 3).unwrap().set_text(qty);
1128            tbl.cell(row, 4).unwrap().set_text(unit);
1129            tbl.cell(row, 5).unwrap().set_text(total);
1130            if i % 2 == 0 {
1131                for c in 0..6 {
1132                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1133                }
1134            }
1135        }
1136    }
1137
1138    doc.add_paragraph("");
1139
1140    // ── Totals ──
1141    {
1142        let mut tbl = doc
1143            .add_table(4, 2)
1144            .borders(BorderStyle::Single, 2, "008B8B")
1145            .width(Length::inches(3.5))
1146            .alignment(Alignment::Right);
1147        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1148        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1149        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1150        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1151        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1152        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1153        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1154        tbl.cell(3, 0).unwrap().shading("E07020");
1155        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1156        tbl.cell(3, 1).unwrap().shading("E07020");
1157    }
1158
1159    doc.add_paragraph("");
1160
1161    // ── Terms & Conditions ──
1162    {
1163        let mut p = doc.add_paragraph("");
1164        p.add_run("Terms & Conditions")
1165            .bold(true)
1166            .size(14.0)
1167            .color("1A3C3C");
1168    }
1169    doc.add_numbered_list_item(
1170        "This quotation is valid for 30 calendar days from the date of issue.",
1171        0,
1172    );
1173    doc.add_numbered_list_item(
1174        "All prices are in USD and exclusive of applicable taxes unless stated.",
1175        0,
1176    );
1177    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1178    doc.add_numbered_list_item(
1179        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1180        0,
1181    );
1182    doc.add_numbered_list_item(
1183        "Warranty: 3-year manufacturer warranty on all hardware components.",
1184        0,
1185    );
1186    doc.add_numbered_list_item(
1187        "Returns subject to 15% restocking fee if initiated after 14 days.",
1188        0,
1189    );
1190
1191    doc.add_paragraph("");
1192    doc.add_paragraph("")
1193        .border_bottom(BorderStyle::Single, 4, "008B8B");
1194    doc.add_paragraph("");
1195    {
1196        let mut p = doc.add_paragraph("");
1197        p.add_run("Accepted by: ").bold(true);
1198        p.add_run("___________________________ Date: ___________");
1199    }
1200    {
1201        let mut p = doc.add_paragraph("");
1202        p.add_run("Print Name: ").bold(true);
1203        p.add_run("___________________________ Title: ___________");
1204    }
1205
1206    doc
1207}
1208
1209// =============================================================================
1210// 4. INVOICE — Crimson + charcoal scheme
1211// =============================================================================
1212fn generate_invoice(_samples_dir: &Path) -> Document {
1213    let mut doc = Document::new();
1214
1215    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1216    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1217    doc.set_margins(
1218        Length::inches(0.75),
1219        Length::inches(0.75),
1220        Length::inches(0.75),
1221        Length::inches(0.75),
1222    );
1223    doc.set_title("Invoice INV-2026-0392");
1224    doc.set_author("Walter White");
1225    doc.set_keywords("invoice, Tensorbee");
1226
1227    doc.set_footer("Tensorbee — Thank you for your business!");
1228
1229    // ── Company & Invoice Header ──
1230    {
1231        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1232        // Company name left, INVOICE right
1233        {
1234            let mut cell = tbl.cell(0, 0).unwrap();
1235            let mut p = cell.add_paragraph("");
1236            p.add_run("TENSORBEE")
1237                .bold(true)
1238                .size(32.0)
1239                .color("B22222")
1240                .font("Helvetica");
1241        }
1242        {
1243            let mut cell = tbl.cell(0, 1).unwrap();
1244            let mut p = cell.add_paragraph("");
1245            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1246        }
1247        tbl.cell(1, 0)
1248            .unwrap()
1249            .set_text("123 Innovation Drive, Suite 400");
1250        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1251        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1252        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1253        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1254        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1255    }
1256
1257    doc.add_paragraph("")
1258        .border_bottom(BorderStyle::Thick, 8, "B22222");
1259    doc.add_paragraph("");
1260
1261    // ── Bill To / Ship To ──
1262    {
1263        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1264        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1265        tbl.cell(0, 0).unwrap().shading("B22222");
1266        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1267        tbl.cell(0, 1).unwrap().shading("B22222");
1268        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1269        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1270        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1271        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1272        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1273        tbl.cell(3, 1)
1274            .unwrap()
1275            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1276    }
1277
1278    doc.add_paragraph("");
1279
1280    // ── Line Items ──
1281    {
1282        let mut tbl = doc
1283            .add_table(8, 5)
1284            .borders(BorderStyle::Single, 2, "B22222")
1285            .width_pct(100.0);
1286        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1287        for (c, h) in hdrs.iter().enumerate() {
1288            tbl.cell(0, c).unwrap().set_text(h);
1289            tbl.cell(0, c).unwrap().shading("B22222");
1290        }
1291        let items = [
1292            (
1293                "ML Infrastructure Setup — Phase 1",
1294                "1",
1295                "$45,000.00",
1296                "$3,881.25",
1297                "$48,881.25",
1298            ),
1299            (
1300                "Data Pipeline Development (160 hrs)",
1301                "160",
1302                "$225.00",
1303                "$3,105.00",
1304                "$39,105.00",
1305            ),
1306            (
1307                "GPU Cluster Configuration",
1308                "1",
1309                "$12,000.00",
1310                "$1,035.00",
1311                "$13,035.00",
1312            ),
1313            (
1314                "API Gateway Implementation",
1315                "1",
1316                "$18,500.00",
1317                "$1,595.63",
1318                "$20,095.63",
1319            ),
1320            (
1321                "Load Testing & QA (80 hrs)",
1322                "80",
1323                "$195.00",
1324                "$1,345.50",
1325                "$16,945.50",
1326            ),
1327            (
1328                "Documentation & Training",
1329                "1",
1330                "$8,000.00",
1331                "$690.00",
1332                "$8,690.00",
1333            ),
1334            (
1335                "Project Management (3 months)",
1336                "3",
1337                "$6,500.00",
1338                "$1,679.63",
1339                "$21,179.63",
1340            ),
1341        ];
1342        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1343            let r = i + 1;
1344            tbl.cell(r, 0).unwrap().set_text(desc);
1345            tbl.cell(r, 1).unwrap().set_text(qty);
1346            tbl.cell(r, 2).unwrap().set_text(unit);
1347            tbl.cell(r, 3).unwrap().set_text(tax);
1348            tbl.cell(r, 4).unwrap().set_text(amt);
1349            if i % 2 == 0 {
1350                for c in 0..5 {
1351                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1352                }
1353            }
1354        }
1355    }
1356
1357    doc.add_paragraph("");
1358
1359    // ── Totals ──
1360    {
1361        let mut tbl = doc
1362            .add_table(4, 2)
1363            .borders(BorderStyle::Single, 2, "B22222")
1364            .width(Length::inches(3.0))
1365            .alignment(Alignment::Right);
1366        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1367        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1368        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1369        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1370        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1371        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1372        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1373        tbl.cell(3, 0).unwrap().shading("B22222");
1374        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1375        tbl.cell(3, 1).unwrap().shading("B22222");
1376    }
1377
1378    doc.add_paragraph("");
1379    doc.add_paragraph("");
1380
1381    // ── Payment Details ──
1382    {
1383        let mut p = doc.add_paragraph("");
1384        p.add_run("Payment Details")
1385            .bold(true)
1386            .size(14.0)
1387            .color("333333");
1388    }
1389    doc.add_paragraph("Bank: Silicon Valley Bank")
1390        .indent_left(Length::inches(0.3));
1391    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1392        .indent_left(Length::inches(0.3));
1393    doc.add_paragraph("Routing: 121140399")
1394        .indent_left(Length::inches(0.3));
1395    doc.add_paragraph("Swift: SVBKUS6S")
1396        .indent_left(Length::inches(0.3));
1397
1398    doc.add_paragraph("");
1399
1400    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1401        .shading("FAF0F0")
1402        .border_all(BorderStyle::Single, 2, "B22222");
1403
1404    doc
1405}
1406
1407// =============================================================================
1408// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1409// =============================================================================
1410fn generate_report(_samples_dir: &Path) -> Document {
1411    let mut doc = Document::new();
1412
1413    // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1414    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1415    doc.set_margins(
1416        Length::inches(1.0),
1417        Length::inches(1.0),
1418        Length::inches(1.0),
1419        Length::inches(1.0),
1420    );
1421    doc.set_title("Q4 2025 Environmental Impact Report");
1422    doc.set_author("Walter White");
1423    doc.set_subject("Quarterly Environmental Report");
1424    doc.set_keywords("environment, sustainability, report, Tensorbee");
1425
1426    doc.set_different_first_page(true);
1427    doc.set_first_page_header("");
1428    doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1429    doc.set_footer("CONFIDENTIAL — Page");
1430
1431    // ── Cover ──
1432    let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1433    doc.add_background_image(&cover_bg, "report_cover.png");
1434
1435    for _ in 0..5 {
1436        doc.add_paragraph("");
1437    }
1438    {
1439        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440        p.add_run("Q4 2025")
1441            .bold(true)
1442            .size(48.0)
1443            .color("FFFFFF")
1444            .font("Georgia");
1445    }
1446    {
1447        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1448        p.add_run("Environmental Impact Report")
1449            .size(24.0)
1450            .color("90EE90")
1451            .font("Georgia")
1452            .italic(true);
1453    }
1454    doc.add_paragraph("");
1455    {
1456        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1457        p.add_run("Tensorbee — Sustainability Division")
1458            .size(14.0)
1459            .color("C0C0C0");
1460    }
1461    {
1462        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1463        p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1464            .size(11.0)
1465            .color("AAAAAA");
1466    }
1467
1468    // ── TOC ──
1469    doc.add_paragraph("").page_break_before(true);
1470    doc.insert_toc(doc.content_count(), 3);
1471
1472    // ── Section 1: Executive Overview ──
1473    doc.add_paragraph("").page_break_before(true);
1474    doc.add_paragraph("Executive Overview").style("Heading1");
1475    doc.add_paragraph(
1476        "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1477         sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1478         to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1479         100% renewable energy in our primary data centers and the launch of our carbon offset \
1480         marketplace.",
1481    )
1482    .first_line_indent(Length::inches(0.3));
1483
1484    // Image: performance chart
1485    let chart_img = create_chart_png(400, 200);
1486    doc.add_paragraph("");
1487    doc.add_picture(
1488        &chart_img,
1489        "performance_chart.png",
1490        Length::inches(5.0),
1491        Length::inches(2.5),
1492    )
1493    .alignment(Alignment::Center);
1494    {
1495        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1496        p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1497            .italic(true)
1498            .size(9.0)
1499            .color("666666");
1500    }
1501
1502    // ── Section 2: Energy Consumption ──
1503    doc.add_paragraph("").page_break_before(true);
1504    doc.add_paragraph("Energy Consumption").style("Heading1");
1505
1506    doc.add_paragraph("Data Center Operations")
1507        .style("Heading2");
1508    doc.add_paragraph(
1509        "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1510         a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1511         server consolidation.",
1512    );
1513
1514    // Data table
1515    {
1516        let mut tbl = doc
1517            .add_table(5, 4)
1518            .borders(BorderStyle::Single, 4, "2D5016")
1519            .width_pct(100.0);
1520        let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1521        for (c, h) in hdrs.iter().enumerate() {
1522            tbl.cell(0, c).unwrap().set_text(h);
1523            tbl.cell(0, c).unwrap().shading("2D5016");
1524        }
1525        let rows = [
1526            ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1527            ("Dublin (EU)", "2.1", "1.2", "1.18"),
1528            ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1529            ("TOTAL", "7.1", "4.2", "1.17 avg"),
1530        ];
1531        for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1532            tbl.cell(i + 1, 0).unwrap().set_text(dc);
1533            tbl.cell(i + 1, 1).unwrap().set_text(cap);
1534            tbl.cell(i + 1, 2).unwrap().set_text(use_);
1535            tbl.cell(i + 1, 3).unwrap().set_text(pue);
1536            if i == 3 {
1537                for c in 0..4 {
1538                    tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1539                }
1540            }
1541        }
1542    }
1543
1544    doc.add_paragraph("");
1545
1546    doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1547    doc.add_paragraph("Breakdown of energy sources across all facilities:");
1548
1549    doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1550    doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1551    doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1552    doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1553
1554    // ── Section 3: Water & Waste ──
1555    doc.add_paragraph("Water & Waste Management")
1556        .style("Heading1");
1557
1558    doc.add_paragraph("Water Usage").style("Heading2");
1559    doc.add_paragraph(
1560        "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1561         Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1562    );
1563
1564    doc.add_paragraph("Waste Reduction").style("Heading2");
1565    doc.add_paragraph("E-waste management results for Q4:")
1566        .keep_with_next(true);
1567    {
1568        let mut tbl = doc
1569            .add_table(4, 3)
1570            .borders(BorderStyle::Single, 2, "6B8E23")
1571            .width_pct(80.0);
1572        tbl.cell(0, 0).unwrap().set_text("Category");
1573        tbl.cell(0, 0).unwrap().shading("6B8E23");
1574        tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1575        tbl.cell(0, 1).unwrap().shading("6B8E23");
1576        tbl.cell(0, 2).unwrap().set_text("Recycled %");
1577        tbl.cell(0, 2).unwrap().shading("6B8E23");
1578        let rows = [
1579            ("Server Hardware", "2,450", "94%"),
1580            ("Networking Equipment", "820", "91%"),
1581            ("Storage Media", "340", "99%"),
1582        ];
1583        for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1584            tbl.cell(i + 1, 0).unwrap().set_text(cat);
1585            tbl.cell(i + 1, 1).unwrap().set_text(wt);
1586            tbl.cell(i + 1, 2).unwrap().set_text(pct);
1587        }
1588    }
1589
1590    // ── Section 4: Initiatives ──
1591    doc.add_paragraph("").page_break_before(true);
1592    doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1593
1594    doc.add_paragraph("Planned Programs").style("Heading2");
1595    doc.add_numbered_list_item(
1596        "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1597        0,
1598    );
1599    doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1600    doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1601    doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1602
1603    doc.add_paragraph("Investment Targets").style("Heading2");
1604    doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1605        .keep_with_next(true);
1606    {
1607        let mut tbl = doc
1608            .add_table(5, 2)
1609            .borders(BorderStyle::Single, 4, "2D5016");
1610        tbl.cell(0, 0).unwrap().set_text("Initiative");
1611        tbl.cell(0, 0).unwrap().shading("2D5016");
1612        tbl.cell(0, 1).unwrap().set_text("Budget");
1613        tbl.cell(0, 1).unwrap().shading("2D5016");
1614        let rows = [
1615            ("Battery Storage", "$1.2M"),
1616            ("Immersion Cooling Pilot", "$800K"),
1617            ("Solar Panel Expansion", "$2.1M"),
1618            ("Carbon Credits", "$500K"),
1619        ];
1620        for (i, (init, budget)) in rows.iter().enumerate() {
1621            tbl.cell(i + 1, 0).unwrap().set_text(init);
1622            tbl.cell(i + 1, 1).unwrap().set_text(budget);
1623            if i % 2 == 0 {
1624                tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1625                tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1626            }
1627        }
1628    }
1629
1630    // Image: sustainability roadmap
1631    let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1632    doc.add_paragraph("");
1633    doc.add_picture(
1634        &roadmap_img,
1635        "roadmap.png",
1636        Length::inches(6.0),
1637        Length::inches(1.2),
1638    )
1639    .alignment(Alignment::Center);
1640    {
1641        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1642        p.add_run("Figure 2: 2026 Sustainability Roadmap")
1643            .italic(true)
1644            .size(9.0)
1645            .color("666666");
1646    }
1647
1648    doc.add_paragraph("");
1649    doc.add_paragraph(
1650        "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1651    )
1652    .shading("FFFAF0")
1653    .border_all(BorderStyle::Single, 2, "2D5016");
1654
1655    doc
1656}
Source

pub fn set_keep_with_next(&mut self, val: bool)

Set keep with next paragraph in place.

Source

pub fn set_keep_with_next_value(&mut self, val: Option<bool>)

Set or clear direct keep-with-next formatting.

Source

pub fn keep_together(self, val: bool) -> Self

Set keep lines together.

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

pub fn set_keep_together(&mut self, val: bool)

Set keep lines together in place.

Source

pub fn set_keep_together_value(&mut self, val: Option<bool>)

Set or clear direct keep-together formatting.

Source

pub fn page_break_before(self, val: bool) -> Self

Set page break before.

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

pub fn set_page_break_before(&mut self, val: bool)

Set page break before in place.

Source

pub fn set_page_break_before_value(&mut self, val: Option<bool>)

Set or clear direct page-break-before formatting.

Source

pub fn widow_control(self, val: bool) -> Self

Set widow/orphan control.

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

pub fn set_widow_control(&mut self, val: bool)

Set widow/orphan control in place.

Source

pub fn set_widow_control_value(&mut self, val: Option<bool>)

Set or clear direct widow-control formatting.

Source

pub fn line_spacing(self, pt: f64) -> Self

Set line spacing in points with “exact” rule.

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

pub fn set_line_spacing(&mut self, pt: f64)

Set exact line spacing in place.

Source

pub fn clear_line_spacing(&mut self)

Clear direct line spacing.

Source

pub fn line_spacing_multiple(self, multiple: f64) -> Self

Set line spacing with a multiplier (1.0 = single, 1.5, 2.0 = double, etc.).

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

pub fn set_line_spacing_multiple(&mut self, multiple: f64)

Set multiplied line spacing in place.

Source

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

Set a background/shading fill color (hex string, e.g. “FFFF00”).

Examples found in repository?
examples/generate_all_samples.rs (line 269)
97fn generate_feature_showcase(_samples_dir: &Path) -> Document {
98    let mut doc = Document::new();
99
100    // ── Page Setup & Metadata ──
101    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
102    doc.set_margins(
103        Length::inches(1.0),
104        Length::inches(1.0),
105        Length::inches(1.0),
106        Length::inches(1.0),
107    );
108    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
109    doc.set_gutter(Length::twips(0));
110    doc.set_title("rdocx Feature Showcase");
111    doc.set_author("rdocx Sample Generator");
112    doc.set_subject("Comprehensive feature demonstration");
113    doc.set_keywords("rdocx, docx, rust, sample, showcase");
114
115    // Headers & Footers with different first page
116    doc.set_different_first_page(true);
117    doc.set_first_page_header("rdocx");
118    doc.set_first_page_footer("Feature Showcase — Cover Page");
119    doc.set_header("rdocx Feature Showcase");
120    doc.set_footer("Generated by rdocx");
121
122    // ── COVER PAGE ──
123    let bg = create_sample_png(612, 792, [20, 45, 90]);
124    doc.add_background_image(&bg, "cover_bg.png");
125
126    for _ in 0..3 {
127        doc.add_paragraph("");
128    }
129    {
130        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
131        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
132    }
133    {
134        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
135        p.add_run("Complete Feature Showcase")
136            .size(28.0)
137            .color("FFFFFF")
138            .italic(true);
139    }
140    doc.add_paragraph("");
141    {
142        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
143        p.add_run("Every feature of the rdocx Rust library")
144            .size(13.0)
145            .color("B0C4DE");
146    }
147    {
148        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
149        p.add_run("demonstrated in a single document.")
150            .size(13.0)
151            .color("B0C4DE");
152    }
153
154    // ── TABLE OF CONTENTS ──
155    doc.add_paragraph("").page_break_before(true);
156    doc.insert_toc(doc.content_count(), 3);
157
158    // ── SECTION 1: TEXT FORMATTING ──
159    doc.add_paragraph("").page_break_before(true);
160    doc.add_paragraph("1. Text Formatting").style("Heading1");
161
162    // Paragraph Alignment
163    doc.add_paragraph("1.1 Paragraph Alignment")
164        .style("Heading2");
165    doc.add_paragraph("Left-aligned paragraph (default).")
166        .alignment(Alignment::Left);
167    doc.add_paragraph("Center-aligned paragraph.")
168        .alignment(Alignment::Center);
169    doc.add_paragraph("Right-aligned paragraph.")
170        .alignment(Alignment::Right);
171    doc.add_paragraph(
172        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
173         distributes extra space across word gaps so lines fill the full width of the text area.",
174    )
175    .alignment(Alignment::Justify);
176
177    doc.add_paragraph("");
178
179    // Run Formatting
180    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
181    {
182        let mut p = doc.add_paragraph("");
183        p.add_run("Bold").bold(true);
184        p.add_run(" | ");
185        p.add_run("Italic").italic(true);
186        p.add_run(" | ");
187        p.add_run("Bold Italic").bold(true).italic(true);
188        p.add_run(" | ");
189        p.add_run("Underline").underline(true);
190        p.add_run(" | ");
191        p.add_run("Strikethrough").strike(true);
192        p.add_run(" | ");
193        p.add_run("Double Strike").double_strike(true);
194    }
195    {
196        let mut p = doc.add_paragraph("");
197        p.add_run("Red").color("FF0000");
198        p.add_run(" | ");
199        p.add_run("Blue").color("0000FF");
200        p.add_run(" | ");
201        p.add_run("Green").color("00AA00");
202        p.add_run(" | ");
203        p.add_run("Highlighted").highlight("FFFF00");
204    }
205    {
206        let mut p = doc.add_paragraph("");
207        p.add_run("8pt").size(8.0);
208        p.add_run(" | ");
209        p.add_run("11pt").size(11.0);
210        p.add_run(" | ");
211        p.add_run("16pt").size(16.0);
212        p.add_run(" | ");
213        p.add_run("24pt").size(24.0);
214    }
215    {
216        let mut p = doc.add_paragraph("");
217        p.add_run("Arial").font("Arial");
218        p.add_run(" | ");
219        p.add_run("Times New Roman").font("Times New Roman");
220        p.add_run(" | ");
221        p.add_run("Courier New").font("Courier New");
222    }
223    {
224        let mut p = doc.add_paragraph("");
225        p.add_run("H");
226        p.add_run("2").subscript();
227        p.add_run("O (subscript) | E=mc");
228        p.add_run("2").superscript();
229        p.add_run(" (superscript)");
230    }
231    {
232        let mut p = doc.add_paragraph("");
233        p.add_run("ALL CAPS").all_caps(true);
234        p.add_run(" | ");
235        p.add_run("Small Caps").small_caps(true);
236        p.add_run(" | ");
237        p.add_run("Expanded").character_spacing(Length::twips(40));
238        p.add_run(" | ");
239        p.add_run("Hidden text (not visible)").hidden(true);
240    }
241
242    doc.add_paragraph("");
243
244    // Underline Styles
245    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
246    {
247        let mut p = doc.add_paragraph("");
248        p.add_run("Single ")
249            .underline_style(rdocx::UnderlineStyle::Single);
250        p.add_run("Double ")
251            .underline_style(rdocx::UnderlineStyle::Double);
252        p.add_run("Thick ")
253            .underline_style(rdocx::UnderlineStyle::Thick);
254        p.add_run("Dotted ")
255            .underline_style(rdocx::UnderlineStyle::Dotted);
256        p.add_run("Dash ")
257            .underline_style(rdocx::UnderlineStyle::Dash);
258        p.add_run("Wave ")
259            .underline_style(rdocx::UnderlineStyle::Wave);
260    }
261
262    // ── SECTION 2: PARAGRAPH FORMATTING ──
263    doc.add_paragraph("").page_break_before(true);
264    doc.add_paragraph("2. Paragraph Formatting")
265        .style("Heading1");
266
267    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
268    doc.add_paragraph("Paragraph with light green shading")
269        .shading("E2EFDA");
270    doc.add_paragraph("Paragraph with bottom border")
271        .border_bottom(BorderStyle::Single, 6, "2E75B6");
272    doc.add_paragraph("Paragraph with all borders (red)")
273        .border_all(BorderStyle::Single, 4, "FF0000");
274
275    doc.add_paragraph("2.2 Indentation").style("Heading2");
276    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
277        .indent_left(Length::inches(1.0))
278        .hanging_indent(Length::inches(0.5));
279    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
280        .first_line_indent(Length::inches(0.5));
281    doc.add_paragraph("Both left and right indent (0.75 inches each)")
282        .indent_left(Length::inches(0.75))
283        .indent_right(Length::inches(0.75));
284
285    doc.add_paragraph("2.3 Spacing & Line Height")
286        .style("Heading2");
287    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
288        .space_before(Length::pt(24.0))
289        .space_after(Length::pt(12.0));
290    doc.add_paragraph(
291        "Double line spacing paragraph. This text should have extra vertical space between \
292         lines to demonstrate line_spacing_multiple(2.0).",
293    )
294    .line_spacing_multiple(2.0);
295    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
296        .line_spacing(20.0);
297
298    doc.add_paragraph("2.4 Pagination Controls")
299        .style("Heading2");
300    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
301        .keep_with_next(true);
302    doc.add_paragraph("(This paragraph was kept with the one above.)");
303    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
304        .keep_together(true);
305    doc.add_paragraph("widow_control — prevents widow/orphan lines")
306        .widow_control(true);
307
308    // ── SECTION 3: LISTS ──
309    doc.add_paragraph("").page_break_before(true);
310    doc.add_paragraph("3. Lists").style("Heading1");
311
312    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
313    doc.add_bullet_list_item("First item", 0);
314    doc.add_bullet_list_item("Second item", 0);
315    doc.add_bullet_list_item("Nested level 1", 1);
316    doc.add_bullet_list_item("Nested level 2", 2);
317    doc.add_bullet_list_item("Back to level 1", 1);
318    doc.add_bullet_list_item("Third item", 0);
319
320    doc.add_paragraph("");
321
322    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
323    doc.add_numbered_list_item("First numbered item", 0);
324    doc.add_numbered_list_item("Second numbered item", 0);
325    doc.add_numbered_list_item("Sub-item A", 1);
326    doc.add_numbered_list_item("Sub-item B", 1);
327    doc.add_numbered_list_item("Third numbered item", 0);
328
329    // ── SECTION 4: TAB STOPS ──
330    doc.add_paragraph("");
331    doc.add_paragraph("4. Tab Stops").style("Heading1");
332
333    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
334    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
335        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
336        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
337        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
338        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
339
340    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
341    doc.add_paragraph("Item\t\tPrice")
342        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
343        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
344        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
345    doc.add_paragraph("Widget\t\t$19.99")
346        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
347        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
348        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
349    doc.add_paragraph("Gadget\t\t$249.50")
350        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
351        .add_tab_stop_with_leader(
352            TabAlignment::Right,
353            Length::inches(4.0),
354            TabLeader::Underscore,
355        )
356        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
357
358    // ── SECTION 5: TABLES ──
359    doc.add_paragraph("").page_break_before(true);
360    doc.add_paragraph("5. Tables").style("Heading1");
361
362    doc.add_paragraph("5.1 Basic Table").style("Heading2");
363    {
364        let mut tbl = doc
365            .add_table(4, 3)
366            .borders(BorderStyle::Single, 4, "000000");
367        for col in 0..3 {
368            tbl.cell(0, col).unwrap().shading("2E75B6");
369        }
370        tbl.cell(0, 0).unwrap().set_text("Name");
371        tbl.cell(0, 1).unwrap().set_text("Role");
372        tbl.cell(0, 2).unwrap().set_text("Location");
373        tbl.cell(1, 0).unwrap().set_text("Walter White");
374        tbl.cell(1, 1).unwrap().set_text("CEO");
375        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
376        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
377        tbl.cell(2, 1).unwrap().set_text("CTO");
378        tbl.cell(2, 2).unwrap().set_text("Remote");
379        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
380        tbl.cell(3, 1).unwrap().set_text("Security");
381        tbl.cell(3, 2).unwrap().set_text("Washington");
382    }
383
384    doc.add_paragraph("");
385
386    doc.add_paragraph("5.2 Column Span & Cell Shading")
387        .style("Heading2");
388    {
389        let mut tbl = doc
390            .add_table(3, 4)
391            .borders(BorderStyle::Single, 4, "000000")
392            .width_pct(100.0);
393        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
394        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
395        tbl.cell(1, 0).unwrap().set_text("Region");
396        tbl.cell(1, 0).unwrap().shading("D6E4F0");
397        tbl.cell(1, 1).unwrap().set_text("Q1");
398        tbl.cell(1, 1).unwrap().shading("D6E4F0");
399        tbl.cell(1, 2).unwrap().set_text("Q2");
400        tbl.cell(1, 2).unwrap().shading("D6E4F0");
401        tbl.cell(1, 3).unwrap().set_text("Total");
402        tbl.cell(1, 3).unwrap().shading("D6E4F0");
403        tbl.cell(2, 0).unwrap().set_text("Americas");
404        tbl.cell(2, 1).unwrap().set_text("$2.4M");
405        tbl.cell(2, 2).unwrap().set_text("$2.7M");
406        tbl.cell(2, 3).unwrap().set_text("$5.1M");
407    }
408
409    doc.add_paragraph("");
410
411    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
412    {
413        let mut tbl = doc
414            .add_table(4, 3)
415            .borders(BorderStyle::Single, 4, "000000");
416        tbl.cell(0, 0).unwrap().set_text("Category");
417        tbl.cell(0, 0).unwrap().shading("E2EFDA");
418        tbl.cell(0, 1).unwrap().set_text("Item");
419        tbl.cell(0, 1).unwrap().shading("E2EFDA");
420        tbl.cell(0, 2).unwrap().set_text("Price");
421        tbl.cell(0, 2).unwrap().shading("E2EFDA");
422        tbl.cell(1, 0).unwrap().set_text("Hardware");
423        tbl.cell(1, 0).unwrap().v_merge_restart();
424        tbl.cell(1, 1).unwrap().set_text("Laptop");
425        tbl.cell(1, 2).unwrap().set_text("$1,200");
426        tbl.cell(2, 0).unwrap().v_merge_continue();
427        tbl.cell(2, 1).unwrap().set_text("Monitor");
428        tbl.cell(2, 2).unwrap().set_text("$450");
429        tbl.cell(3, 0).unwrap().set_text("Software");
430        tbl.cell(3, 1).unwrap().set_text("IDE License");
431        tbl.cell(3, 2).unwrap().set_text("$200/yr");
432    }
433
434    doc.add_paragraph("");
435
436    doc.add_paragraph("5.4 Nested Table").style("Heading2");
437    {
438        let mut tbl = doc
439            .add_table(2, 2)
440            .borders(BorderStyle::Single, 6, "2E75B6");
441        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
442        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
443        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
444        {
445            let mut cell = tbl.cell(1, 1).unwrap();
446            cell.set_text("Nested table below:");
447            let mut nested = cell
448                .add_table(2, 2)
449                .borders(BorderStyle::Single, 2, "FF6600");
450            nested.cell(0, 0).unwrap().set_text("A");
451            nested.cell(0, 1).unwrap().set_text("B");
452            nested.cell(1, 0).unwrap().set_text("C");
453            nested.cell(1, 1).unwrap().set_text("D");
454        }
455    }
456
457    doc.add_paragraph("");
458
459    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
460        .style("Heading2");
461    {
462        let mut tbl = doc
463            .add_table(2, 3)
464            .borders(BorderStyle::Single, 4, "666666");
465        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
466        tbl.cell(0, 0).unwrap().set_text("Top");
467        tbl.cell(0, 0)
468            .unwrap()
469            .vertical_alignment(VerticalAlignment::Top)
470            .shading("FFF2CC");
471        tbl.cell(0, 1).unwrap().set_text("Center");
472        tbl.cell(0, 1)
473            .unwrap()
474            .vertical_alignment(VerticalAlignment::Center)
475            .shading("D9E2F3");
476        tbl.cell(0, 2).unwrap().set_text("Bottom");
477        tbl.cell(0, 2)
478            .unwrap()
479            .vertical_alignment(VerticalAlignment::Bottom)
480            .shading("E2EFDA");
481        tbl.row(1).unwrap().cant_split();
482        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
483        tbl.cell(1, 0).unwrap().no_wrap();
484        tbl.cell(1, 1).unwrap().set_text("Fixed width");
485        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
486        tbl.cell(1, 2).unwrap().set_text("Normal");
487    }
488
489    // ── SECTION 6: IMAGES ──
490    doc.add_paragraph("").page_break_before(true);
491    doc.add_paragraph("6. Images").style("Heading1");
492
493    doc.add_paragraph("6.1 Inline Image").style("Heading2");
494    doc.add_paragraph("A blue gradient image below:");
495    let img = create_sample_png(200, 50, [0, 80, 200]);
496    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
497
498    doc.add_paragraph("");
499    doc.add_paragraph("6.2 Header Image").style("Heading2");
500    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
501    doc.set_header_image(
502        &hdr_img,
503        "header_logo.png",
504        Length::inches(2.0),
505        Length::inches(0.2),
506    );
507    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
508
509    doc.add_paragraph("");
510    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
511
512    // ── SECTION 7: CONTENT MANIPULATION ──
513    doc.add_paragraph("").page_break_before(true);
514    doc.add_paragraph("7. Content Manipulation")
515        .style("Heading1");
516
517    doc.add_paragraph("7.1 Placeholder Replacement")
518        .style("Heading2");
519    doc.add_paragraph("Customer: {{customer}}");
520    doc.add_paragraph("Date: {{date}}");
521    doc.add_paragraph("Reference: {{ref_number}}");
522    {
523        let mut tbl = doc
524            .add_table(2, 2)
525            .borders(BorderStyle::Single, 4, "000000");
526        tbl.cell(0, 0).unwrap().set_text("Project");
527        tbl.cell(0, 0).unwrap().shading("D6E4F0");
528        tbl.cell(0, 1).unwrap().set_text("{{project}}");
529        tbl.cell(1, 0).unwrap().set_text("Status");
530        tbl.cell(1, 0).unwrap().shading("D6E4F0");
531        tbl.cell(1, 1).unwrap().set_text("{{status}}");
532    }
533
534    let mut replacements = HashMap::new();
535    replacements.insert("{{customer}}", "Tensorbee Inc.");
536    replacements.insert("{{date}}", "February 22, 2026");
537    replacements.insert("{{ref_number}}", "TB-2026-001");
538    replacements.insert("{{project}}", "Infrastructure Upgrade");
539    replacements.insert("{{status}}", "In Progress");
540    let n = doc.replace_all(&replacements);
541    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
542
543    doc.add_paragraph("");
544
545    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
546    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
547    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
548    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
549
550    doc.add_paragraph("");
551
552    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
553    doc.add_paragraph("Section A: First content.");
554    doc.add_paragraph("Section C: Third content.");
555    if let Some(idx) = doc.find_content_index("Section C") {
556        doc.insert_paragraph(
557            idx,
558            "Section B: Inserted between A and C via find_content_index().",
559        );
560    }
561
562    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
563    doc.add_paragraph("").section_break(SectionBreak::NextPage);
564    doc.add_paragraph("8. Section Breaks & Orientation")
565        .style("Heading1");
566    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
567
568    {
569        let mut tbl = doc
570            .add_table(3, 7)
571            .borders(BorderStyle::Single, 4, "2E75B6");
572        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
573        for (c, h) in headers.iter().enumerate() {
574            tbl.cell(0, c).unwrap().set_text(h);
575            tbl.cell(0, c).unwrap().shading("2E75B6");
576        }
577        let data = [
578            [
579                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
580            ],
581            [
582                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
583            ],
584        ];
585        for (r, row) in data.iter().enumerate() {
586            for (c, v) in row.iter().enumerate() {
587                tbl.cell(r + 1, c).unwrap().set_text(v);
588            }
589        }
590    }
591
592    doc.add_paragraph("")
593        .section_break(SectionBreak::NextPage)
594        .section_landscape();
595
596    // ── SECTION 9: CUSTOM STYLES ──
597    doc.add_paragraph("9. Custom Styles").style("Heading1");
598    doc.add_style(
599        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
600            .based_on("Normal")
601            .paragraph_properties(rdocx_oxml::properties::CT_PPr {
602                shading: Some(rdocx_oxml::properties::CT_Shd {
603                    val: "clear".to_string(),
604                    color: None,
605                    fill: Some("FFF2CC".to_string()),
606                }),
607                ..Default::default()
608            })
609            .run_properties(rdocx_oxml::properties::CT_RPr {
610                bold: Some(true),
611                color: Some("C45911".to_string()),
612                ..Default::default()
613            }),
614    );
615    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
616        .style("CustomHighlight");
617
618    doc.add_paragraph("");
619
620    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
621    doc.add_paragraph("10. Document Intelligence API")
622        .style("Heading1");
623
624    let wc = doc.word_count();
625    let hc = doc.headings().len();
626    let ic = doc.images().len();
627    let lc = doc.links().len();
628    doc.add_paragraph(&format!("Word count: {wc}"));
629    doc.add_paragraph(&format!("Heading count: {hc}"));
630    doc.add_paragraph(&format!("Image count: {ic}"));
631    doc.add_paragraph(&format!("Link count: {lc}"));
632
633    let outline = doc.document_outline();
634    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
635
636    let issues = doc.audit_accessibility();
637    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
638    for issue in &issues {
639        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
640    }
641
642    // ── SECTION 11: DOCUMENT MERGING ──
643    doc.add_paragraph("");
644    doc.add_paragraph("11. Document Merging").style("Heading1");
645    {
646        let mut other = Document::new();
647        other.add_paragraph("This paragraph was merged from another document using append().");
648        doc.append(&other);
649    }
650
651    doc.add_paragraph("");
652
653    // ── SUMMARY ──
654    doc.add_paragraph("Summary of Demonstrated Features")
655        .style("Heading1");
656    let features = [
657        "Page setup: size, margins, header/footer distance, gutter",
658        "Document metadata: title, author, subject, keywords",
659        "Headers and footers: text, images, different first page",
660        "Background images (full-page behind text)",
661        "Table of Contents generation with bookmarks",
662        "Text formatting: bold, italic, underline styles, strike, color, size, font",
663        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
664        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
665        "Pagination controls: keep-with-next, keep-together, widow control",
666        "Bullet and numbered lists with nesting",
667        "Tab stops with dot/underscore/hyphen leaders",
668        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
669        "Row properties: header rows, exact height, cant-split",
670        "Cell properties: width, no-wrap, vertical alignment",
671        "Inline images and header images",
672        "Placeholder replacement (single and batch)",
673        "Regex-based find and replace",
674        "Content insertion at specific positions",
675        "Section breaks with mixed portrait/landscape",
676        "Custom paragraph and character styles",
677        "Document intelligence: word count, headings, outline, images, links",
678        "Accessibility audit",
679        "Document merging (append)",
680        "Export: DOCX, PDF, HTML, Markdown",
681    ];
682    for f in &features {
683        doc.add_bullet_list_item(f, 0);
684    }
685    doc.add_paragraph("");
686    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
687        .alignment(Alignment::Center)
688        .shading("E2EFDA")
689        .border_all(BorderStyle::Single, 2, "00AA00");
690
691    doc
692}
693
694// =============================================================================
695// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
696// =============================================================================
697fn generate_proposal(_samples_dir: &Path) -> Document {
698    let mut doc = Document::new();
699
700    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
701    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
702    doc.set_margins(
703        Length::inches(1.0),
704        Length::inches(1.0),
705        Length::inches(1.0),
706        Length::inches(1.0),
707    );
708    doc.set_title("AI Platform Modernization Proposal");
709    doc.set_author("Walter White");
710    doc.set_subject("Technology Proposal");
711    doc.set_keywords("proposal, AI, modernization, Tensorbee");
712
713    // Banner header
714    let logo_img = create_logo_png(220, 48);
715    let banner = build_header_banner_xml(
716        "rId1",
717        &BannerOpts {
718            bg_color: "1B2A4A",
719            banner_width: 7772400,
720            banner_height: 914400,
721            logo_width: 2011680,
722            logo_height: 438912,
723            logo_x_offset: 295125,
724            logo_y_offset: 237744,
725        },
726    );
727    doc.set_raw_header_with_images(
728        banner,
729        &[("rId1", &logo_img, "logo.png")],
730        rdocx_oxml::header_footer::HdrFtrType::Default,
731    );
732    doc.set_different_first_page(true);
733    let cover_banner = build_header_banner_xml(
734        "rId1",
735        &BannerOpts {
736            bg_color: "C5922E",
737            banner_width: 7772400,
738            banner_height: 914400,
739            logo_width: 2011680,
740            logo_height: 438912,
741            logo_x_offset: 295125,
742            logo_y_offset: 237744,
743        },
744    );
745    doc.set_raw_header_with_images(
746        cover_banner,
747        &[("rId1", &logo_img, "logo.png")],
748        rdocx_oxml::header_footer::HdrFtrType::First,
749    );
750    doc.set_margins(
751        Length::twips(2292),
752        Length::twips(1440),
753        Length::twips(1440),
754        Length::twips(1440),
755    );
756    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
757    doc.set_footer("Tensorbee — Confidential");
758
759    // Custom styles
760    doc.add_style(
761        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
762            .based_on("Normal")
763            .run_properties(rdocx_oxml::properties::CT_RPr {
764                bold: Some(true),
765                font_ascii: Some("Georgia".to_string()),
766                font_hansi: Some("Georgia".to_string()),
767                sz: Some(rdocx_oxml::HalfPoint(56)), // half-points → 28pt
768                color: Some("1B2A4A".to_string()),
769                ..Default::default()
770            }),
771    );
772
773    // ── Cover Page ──
774    for _ in 0..4 {
775        doc.add_paragraph("");
776    }
777    doc.add_paragraph("AI Platform Modernization")
778        .style("ProposalTitle")
779        .alignment(Alignment::Center);
780    doc.add_paragraph("")
781        .alignment(Alignment::Center)
782        .border_bottom(BorderStyle::Single, 8, "C5922E");
783    {
784        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
785        p.add_run("Prepared for Global Financial Corp.")
786            .size(14.0)
787            .color("666666")
788            .italic(true);
789    }
790    {
791        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
792        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
793            .size(12.0)
794            .color("888888");
795    }
796    {
797        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
798        p.add_run("February 22, 2026").size(12.0).color("888888");
799    }
800
801    // ── TOC ──
802    doc.add_paragraph("").page_break_before(true);
803    doc.add_paragraph("Table of Contents").style("Heading1");
804    // We'll add a manual-style TOC since the insert_toc goes at a specific position
805    doc.insert_toc(doc.content_count(), 2);
806
807    // ── Executive Summary ──
808    doc.add_paragraph("").page_break_before(true);
809    doc.add_paragraph("Executive Summary").style("Heading1");
810    doc.add_paragraph(
811        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
812         transitioning from legacy batch-processing systems to a real-time inference architecture. \
813         This transformation will reduce model deployment time from weeks to hours, improve \
814         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
815    )
816    .first_line_indent(Length::inches(0.3));
817    doc.add_paragraph(
818        "The project spans three phases over 18 months, with a total investment of $2.8M. \
819         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
820         similar transformations for three Fortune 500 financial institutions.",
821    )
822    .first_line_indent(Length::inches(0.3));
823
824    // Key metrics table
825    doc.add_paragraph("");
826    {
827        let mut tbl = doc
828            .add_table(5, 2)
829            .borders(BorderStyle::Single, 4, "1B2A4A")
830            .width_pct(80.0);
831        tbl.cell(0, 0).unwrap().set_text("Key Metric");
832        tbl.cell(0, 0).unwrap().shading("1B2A4A");
833        tbl.cell(0, 1).unwrap().set_text("Value");
834        tbl.cell(0, 1).unwrap().shading("1B2A4A");
835        let rows = [
836            ("Total Investment", "$2.8M"),
837            ("Annual Savings", "$4.2M"),
838            ("ROI (Year 1)", "150%"),
839            ("Timeline", "18 months"),
840        ];
841        for (i, (k, v)) in rows.iter().enumerate() {
842            tbl.cell(i + 1, 0).unwrap().set_text(k);
843            if i % 2 == 0 {
844                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
845            }
846            tbl.cell(i + 1, 1).unwrap().set_text(v);
847            if i % 2 == 0 {
848                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
849            }
850        }
851    }
852
853    // ── Problem Statement ──
854    doc.add_paragraph("").page_break_before(true);
855    doc.add_paragraph("Problem Statement").style("Heading1");
856    doc.add_paragraph(
857        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
858    );
859    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);
860    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
861    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
862
863    // ── Proposed Solution ──
864    doc.add_paragraph("").page_break_before(true);
865    doc.add_paragraph("Proposed Solution").style("Heading1");
866
867    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
868        .style("Heading2");
869    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
870    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
871    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
872
873    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
874        .style("Heading2");
875    doc.add_bullet_list_item(
876        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
877        0,
878    );
879    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
880    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
881
882    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
883        .style("Heading2");
884    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
885    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
886    doc.add_bullet_list_item(
887        "Self-service model deployment portal for data science teams",
888        0,
889    );
890
891    // ── Budget ──
892    doc.add_paragraph("Budget Breakdown").style("Heading1");
893    {
894        let mut tbl = doc
895            .add_table(6, 3)
896            .borders(BorderStyle::Single, 4, "1B2A4A")
897            .width_pct(100.0);
898        tbl.cell(0, 0).unwrap().set_text("Category");
899        tbl.cell(0, 0).unwrap().shading("1B2A4A");
900        tbl.cell(0, 1).unwrap().set_text("Cost");
901        tbl.cell(0, 1).unwrap().shading("1B2A4A");
902        tbl.cell(0, 2).unwrap().set_text("Notes");
903        tbl.cell(0, 2).unwrap().shading("1B2A4A");
904        let items = [
905            (
906                "Infrastructure",
907                "$450,000",
908                "Cloud compute + GPU instances",
909            ),
910            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
911            (
912                "Software Licenses",
913                "$350,000",
914                "Kafka, monitoring, CI/CD tools",
915            ),
916            (
917                "Training & Enablement",
918                "$200,000",
919                "Team training, documentation",
920            ),
921            ("Contingency (10%)", "$200,000", "Risk buffer"),
922        ];
923        for (i, (cat, cost, note)) in items.iter().enumerate() {
924            tbl.cell(i + 1, 0).unwrap().set_text(cat);
925            tbl.cell(i + 1, 1).unwrap().set_text(cost);
926            tbl.cell(i + 1, 2).unwrap().set_text(note);
927            if i % 2 == 0 {
928                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
929                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
930                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
931            }
932        }
933    }
934
935    // ── Closing ──
936    doc.add_paragraph("");
937    doc.add_paragraph("Next Steps").style("Heading1");
938    doc.add_numbered_list_item(
939        "Schedule technical deep-dive with GFC engineering team (Week 1)",
940        0,
941    );
942    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
943    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
944
945    doc.add_paragraph("");
946    {
947        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
948        p.add_run("Walter White")
949            .bold(true)
950            .size(14.0)
951            .color("1B2A4A");
952    }
953    {
954        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
955        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
956            .size(10.0)
957            .color("888888");
958    }
959
960    doc
961}
962
963// =============================================================================
964// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
965// =============================================================================
966fn generate_quote(_samples_dir: &Path) -> Document {
967    let mut doc = Document::new();
968
969    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
970    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
971    doc.set_margins(
972        Length::inches(0.75),
973        Length::inches(0.75),
974        Length::inches(0.75),
975        Length::inches(0.75),
976    );
977    doc.set_title("Quotation QT-2026-0147");
978    doc.set_author("Walter White");
979    doc.set_keywords("quote, BOM, Tensorbee");
980
981    doc.set_header("Tensorbee — Quotation");
982    doc.set_footer("QT-2026-0147 | Page");
983
984    // ── Header Block ──
985    {
986        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
987        p.add_run("TENSORBEE")
988            .bold(true)
989            .size(28.0)
990            .color("008B8B")
991            .font("Helvetica");
992    }
993    doc.add_paragraph("123 Innovation Drive, Suite 400")
994        .alignment(Alignment::Left);
995    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
996        .alignment(Alignment::Left);
997    doc.add_paragraph("")
998        .border_bottom(BorderStyle::Single, 8, "008B8B");
999
1000    // Quote meta
1001    doc.add_paragraph("");
1002    {
1003        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
1004        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
1005        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
1006        tbl.cell(0, 2).unwrap().set_text("DATE");
1007        tbl.cell(0, 2).unwrap().shading("008B8B");
1008        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1009        tbl.cell(0, 3).unwrap().shading("008B8B");
1010        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1011        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1012        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1013        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1014        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1015        tbl.cell(2, 1).unwrap().set_text("Walter White");
1016        tbl.cell(2, 2).unwrap().set_text("Payment:");
1017        tbl.cell(2, 3).unwrap().set_text("Net 30");
1018        tbl.cell(3, 0).unwrap().set_text("Customer:");
1019        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1020        tbl.cell(3, 2).unwrap().set_text("Currency:");
1021        tbl.cell(3, 3).unwrap().set_text("USD");
1022    }
1023
1024    doc.add_paragraph("");
1025
1026    // ── Bill of Materials ──
1027    {
1028        let mut p = doc.add_paragraph("");
1029        p.add_run("Bill of Materials")
1030            .bold(true)
1031            .size(16.0)
1032            .color("1A3C3C");
1033    }
1034    doc.add_paragraph("");
1035
1036    {
1037        let mut tbl = doc
1038            .add_table(10, 6)
1039            .borders(BorderStyle::Single, 2, "008B8B")
1040            .width_pct(100.0)
1041            .layout_fixed();
1042        // Headers
1043        let hdrs = [
1044            "#",
1045            "Part Number",
1046            "Description",
1047            "Qty",
1048            "Unit Price",
1049            "Total",
1050        ];
1051        for (c, h) in hdrs.iter().enumerate() {
1052            tbl.cell(0, c).unwrap().set_text(h);
1053            tbl.cell(0, c).unwrap().shading("008B8B");
1054        }
1055
1056        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1057            (
1058                "TB-GPU-A100",
1059                "NVIDIA A100 80GB GPU",
1060                "4",
1061                "$12,500.00",
1062                "$50,000.00",
1063            ),
1064            (
1065                "TB-SRV-R750",
1066                "Dell R750xa Server Chassis",
1067                "2",
1068                "$8,200.00",
1069                "$16,400.00",
1070            ),
1071            (
1072                "TB-RAM-256G",
1073                "256GB DDR5 ECC Memory Module",
1074                "8",
1075                "$890.00",
1076                "$7,120.00",
1077            ),
1078            (
1079                "TB-SSD-3840",
1080                "3.84TB NVMe U.2 SSD",
1081                "8",
1082                "$1,150.00",
1083                "$9,200.00",
1084            ),
1085            (
1086                "TB-NET-CX7",
1087                "ConnectX-7 200GbE NIC",
1088                "4",
1089                "$1,800.00",
1090                "$7,200.00",
1091            ),
1092            (
1093                "TB-SW-48P",
1094                "48-Port 100GbE Switch",
1095                "1",
1096                "$22,000.00",
1097                "$22,000.00",
1098            ),
1099            (
1100                "TB-CAB-RACK",
1101                "42U Server Rack + PDU",
1102                "1",
1103                "$4,500.00",
1104                "$4,500.00",
1105            ),
1106            (
1107                "TB-SVC-INST",
1108                "Installation & Configuration",
1109                "1",
1110                "$8,500.00",
1111                "$8,500.00",
1112            ),
1113            (
1114                "TB-SVC-SUPP",
1115                "3-Year Premium Support",
1116                "1",
1117                "$15,000.00",
1118                "$15,000.00",
1119            ),
1120        ];
1121
1122        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1123            let row = i + 1;
1124            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1125            tbl.cell(row, 1).unwrap().set_text(pn);
1126            tbl.cell(row, 2).unwrap().set_text(desc);
1127            tbl.cell(row, 3).unwrap().set_text(qty);
1128            tbl.cell(row, 4).unwrap().set_text(unit);
1129            tbl.cell(row, 5).unwrap().set_text(total);
1130            if i % 2 == 0 {
1131                for c in 0..6 {
1132                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1133                }
1134            }
1135        }
1136    }
1137
1138    doc.add_paragraph("");
1139
1140    // ── Totals ──
1141    {
1142        let mut tbl = doc
1143            .add_table(4, 2)
1144            .borders(BorderStyle::Single, 2, "008B8B")
1145            .width(Length::inches(3.5))
1146            .alignment(Alignment::Right);
1147        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1148        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1149        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1150        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1151        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1152        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1153        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1154        tbl.cell(3, 0).unwrap().shading("E07020");
1155        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1156        tbl.cell(3, 1).unwrap().shading("E07020");
1157    }
1158
1159    doc.add_paragraph("");
1160
1161    // ── Terms & Conditions ──
1162    {
1163        let mut p = doc.add_paragraph("");
1164        p.add_run("Terms & Conditions")
1165            .bold(true)
1166            .size(14.0)
1167            .color("1A3C3C");
1168    }
1169    doc.add_numbered_list_item(
1170        "This quotation is valid for 30 calendar days from the date of issue.",
1171        0,
1172    );
1173    doc.add_numbered_list_item(
1174        "All prices are in USD and exclusive of applicable taxes unless stated.",
1175        0,
1176    );
1177    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1178    doc.add_numbered_list_item(
1179        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1180        0,
1181    );
1182    doc.add_numbered_list_item(
1183        "Warranty: 3-year manufacturer warranty on all hardware components.",
1184        0,
1185    );
1186    doc.add_numbered_list_item(
1187        "Returns subject to 15% restocking fee if initiated after 14 days.",
1188        0,
1189    );
1190
1191    doc.add_paragraph("");
1192    doc.add_paragraph("")
1193        .border_bottom(BorderStyle::Single, 4, "008B8B");
1194    doc.add_paragraph("");
1195    {
1196        let mut p = doc.add_paragraph("");
1197        p.add_run("Accepted by: ").bold(true);
1198        p.add_run("___________________________ Date: ___________");
1199    }
1200    {
1201        let mut p = doc.add_paragraph("");
1202        p.add_run("Print Name: ").bold(true);
1203        p.add_run("___________________________ Title: ___________");
1204    }
1205
1206    doc
1207}
1208
1209// =============================================================================
1210// 4. INVOICE — Crimson + charcoal scheme
1211// =============================================================================
1212fn generate_invoice(_samples_dir: &Path) -> Document {
1213    let mut doc = Document::new();
1214
1215    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1216    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1217    doc.set_margins(
1218        Length::inches(0.75),
1219        Length::inches(0.75),
1220        Length::inches(0.75),
1221        Length::inches(0.75),
1222    );
1223    doc.set_title("Invoice INV-2026-0392");
1224    doc.set_author("Walter White");
1225    doc.set_keywords("invoice, Tensorbee");
1226
1227    doc.set_footer("Tensorbee — Thank you for your business!");
1228
1229    // ── Company & Invoice Header ──
1230    {
1231        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1232        // Company name left, INVOICE right
1233        {
1234            let mut cell = tbl.cell(0, 0).unwrap();
1235            let mut p = cell.add_paragraph("");
1236            p.add_run("TENSORBEE")
1237                .bold(true)
1238                .size(32.0)
1239                .color("B22222")
1240                .font("Helvetica");
1241        }
1242        {
1243            let mut cell = tbl.cell(0, 1).unwrap();
1244            let mut p = cell.add_paragraph("");
1245            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1246        }
1247        tbl.cell(1, 0)
1248            .unwrap()
1249            .set_text("123 Innovation Drive, Suite 400");
1250        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1251        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1252        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1253        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1254        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1255    }
1256
1257    doc.add_paragraph("")
1258        .border_bottom(BorderStyle::Thick, 8, "B22222");
1259    doc.add_paragraph("");
1260
1261    // ── Bill To / Ship To ──
1262    {
1263        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1264        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1265        tbl.cell(0, 0).unwrap().shading("B22222");
1266        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1267        tbl.cell(0, 1).unwrap().shading("B22222");
1268        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1269        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1270        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1271        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1272        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1273        tbl.cell(3, 1)
1274            .unwrap()
1275            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1276    }
1277
1278    doc.add_paragraph("");
1279
1280    // ── Line Items ──
1281    {
1282        let mut tbl = doc
1283            .add_table(8, 5)
1284            .borders(BorderStyle::Single, 2, "B22222")
1285            .width_pct(100.0);
1286        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1287        for (c, h) in hdrs.iter().enumerate() {
1288            tbl.cell(0, c).unwrap().set_text(h);
1289            tbl.cell(0, c).unwrap().shading("B22222");
1290        }
1291        let items = [
1292            (
1293                "ML Infrastructure Setup — Phase 1",
1294                "1",
1295                "$45,000.00",
1296                "$3,881.25",
1297                "$48,881.25",
1298            ),
1299            (
1300                "Data Pipeline Development (160 hrs)",
1301                "160",
1302                "$225.00",
1303                "$3,105.00",
1304                "$39,105.00",
1305            ),
1306            (
1307                "GPU Cluster Configuration",
1308                "1",
1309                "$12,000.00",
1310                "$1,035.00",
1311                "$13,035.00",
1312            ),
1313            (
1314                "API Gateway Implementation",
1315                "1",
1316                "$18,500.00",
1317                "$1,595.63",
1318                "$20,095.63",
1319            ),
1320            (
1321                "Load Testing & QA (80 hrs)",
1322                "80",
1323                "$195.00",
1324                "$1,345.50",
1325                "$16,945.50",
1326            ),
1327            (
1328                "Documentation & Training",
1329                "1",
1330                "$8,000.00",
1331                "$690.00",
1332                "$8,690.00",
1333            ),
1334            (
1335                "Project Management (3 months)",
1336                "3",
1337                "$6,500.00",
1338                "$1,679.63",
1339                "$21,179.63",
1340            ),
1341        ];
1342        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1343            let r = i + 1;
1344            tbl.cell(r, 0).unwrap().set_text(desc);
1345            tbl.cell(r, 1).unwrap().set_text(qty);
1346            tbl.cell(r, 2).unwrap().set_text(unit);
1347            tbl.cell(r, 3).unwrap().set_text(tax);
1348            tbl.cell(r, 4).unwrap().set_text(amt);
1349            if i % 2 == 0 {
1350                for c in 0..5 {
1351                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1352                }
1353            }
1354        }
1355    }
1356
1357    doc.add_paragraph("");
1358
1359    // ── Totals ──
1360    {
1361        let mut tbl = doc
1362            .add_table(4, 2)
1363            .borders(BorderStyle::Single, 2, "B22222")
1364            .width(Length::inches(3.0))
1365            .alignment(Alignment::Right);
1366        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1367        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1368        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1369        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1370        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1371        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1372        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1373        tbl.cell(3, 0).unwrap().shading("B22222");
1374        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1375        tbl.cell(3, 1).unwrap().shading("B22222");
1376    }
1377
1378    doc.add_paragraph("");
1379    doc.add_paragraph("");
1380
1381    // ── Payment Details ──
1382    {
1383        let mut p = doc.add_paragraph("");
1384        p.add_run("Payment Details")
1385            .bold(true)
1386            .size(14.0)
1387            .color("333333");
1388    }
1389    doc.add_paragraph("Bank: Silicon Valley Bank")
1390        .indent_left(Length::inches(0.3));
1391    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1392        .indent_left(Length::inches(0.3));
1393    doc.add_paragraph("Routing: 121140399")
1394        .indent_left(Length::inches(0.3));
1395    doc.add_paragraph("Swift: SVBKUS6S")
1396        .indent_left(Length::inches(0.3));
1397
1398    doc.add_paragraph("");
1399
1400    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1401        .shading("FAF0F0")
1402        .border_all(BorderStyle::Single, 2, "B22222");
1403
1404    doc
1405}
1406
1407// =============================================================================
1408// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1409// =============================================================================
1410fn generate_report(_samples_dir: &Path) -> Document {
1411    let mut doc = Document::new();
1412
1413    // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1414    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1415    doc.set_margins(
1416        Length::inches(1.0),
1417        Length::inches(1.0),
1418        Length::inches(1.0),
1419        Length::inches(1.0),
1420    );
1421    doc.set_title("Q4 2025 Environmental Impact Report");
1422    doc.set_author("Walter White");
1423    doc.set_subject("Quarterly Environmental Report");
1424    doc.set_keywords("environment, sustainability, report, Tensorbee");
1425
1426    doc.set_different_first_page(true);
1427    doc.set_first_page_header("");
1428    doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1429    doc.set_footer("CONFIDENTIAL — Page");
1430
1431    // ── Cover ──
1432    let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1433    doc.add_background_image(&cover_bg, "report_cover.png");
1434
1435    for _ in 0..5 {
1436        doc.add_paragraph("");
1437    }
1438    {
1439        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440        p.add_run("Q4 2025")
1441            .bold(true)
1442            .size(48.0)
1443            .color("FFFFFF")
1444            .font("Georgia");
1445    }
1446    {
1447        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1448        p.add_run("Environmental Impact Report")
1449            .size(24.0)
1450            .color("90EE90")
1451            .font("Georgia")
1452            .italic(true);
1453    }
1454    doc.add_paragraph("");
1455    {
1456        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1457        p.add_run("Tensorbee — Sustainability Division")
1458            .size(14.0)
1459            .color("C0C0C0");
1460    }
1461    {
1462        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1463        p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1464            .size(11.0)
1465            .color("AAAAAA");
1466    }
1467
1468    // ── TOC ──
1469    doc.add_paragraph("").page_break_before(true);
1470    doc.insert_toc(doc.content_count(), 3);
1471
1472    // ── Section 1: Executive Overview ──
1473    doc.add_paragraph("").page_break_before(true);
1474    doc.add_paragraph("Executive Overview").style("Heading1");
1475    doc.add_paragraph(
1476        "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1477         sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1478         to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1479         100% renewable energy in our primary data centers and the launch of our carbon offset \
1480         marketplace.",
1481    )
1482    .first_line_indent(Length::inches(0.3));
1483
1484    // Image: performance chart
1485    let chart_img = create_chart_png(400, 200);
1486    doc.add_paragraph("");
1487    doc.add_picture(
1488        &chart_img,
1489        "performance_chart.png",
1490        Length::inches(5.0),
1491        Length::inches(2.5),
1492    )
1493    .alignment(Alignment::Center);
1494    {
1495        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1496        p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1497            .italic(true)
1498            .size(9.0)
1499            .color("666666");
1500    }
1501
1502    // ── Section 2: Energy Consumption ──
1503    doc.add_paragraph("").page_break_before(true);
1504    doc.add_paragraph("Energy Consumption").style("Heading1");
1505
1506    doc.add_paragraph("Data Center Operations")
1507        .style("Heading2");
1508    doc.add_paragraph(
1509        "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1510         a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1511         server consolidation.",
1512    );
1513
1514    // Data table
1515    {
1516        let mut tbl = doc
1517            .add_table(5, 4)
1518            .borders(BorderStyle::Single, 4, "2D5016")
1519            .width_pct(100.0);
1520        let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1521        for (c, h) in hdrs.iter().enumerate() {
1522            tbl.cell(0, c).unwrap().set_text(h);
1523            tbl.cell(0, c).unwrap().shading("2D5016");
1524        }
1525        let rows = [
1526            ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1527            ("Dublin (EU)", "2.1", "1.2", "1.18"),
1528            ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1529            ("TOTAL", "7.1", "4.2", "1.17 avg"),
1530        ];
1531        for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1532            tbl.cell(i + 1, 0).unwrap().set_text(dc);
1533            tbl.cell(i + 1, 1).unwrap().set_text(cap);
1534            tbl.cell(i + 1, 2).unwrap().set_text(use_);
1535            tbl.cell(i + 1, 3).unwrap().set_text(pue);
1536            if i == 3 {
1537                for c in 0..4 {
1538                    tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1539                }
1540            }
1541        }
1542    }
1543
1544    doc.add_paragraph("");
1545
1546    doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1547    doc.add_paragraph("Breakdown of energy sources across all facilities:");
1548
1549    doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1550    doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1551    doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1552    doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1553
1554    // ── Section 3: Water & Waste ──
1555    doc.add_paragraph("Water & Waste Management")
1556        .style("Heading1");
1557
1558    doc.add_paragraph("Water Usage").style("Heading2");
1559    doc.add_paragraph(
1560        "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1561         Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1562    );
1563
1564    doc.add_paragraph("Waste Reduction").style("Heading2");
1565    doc.add_paragraph("E-waste management results for Q4:")
1566        .keep_with_next(true);
1567    {
1568        let mut tbl = doc
1569            .add_table(4, 3)
1570            .borders(BorderStyle::Single, 2, "6B8E23")
1571            .width_pct(80.0);
1572        tbl.cell(0, 0).unwrap().set_text("Category");
1573        tbl.cell(0, 0).unwrap().shading("6B8E23");
1574        tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1575        tbl.cell(0, 1).unwrap().shading("6B8E23");
1576        tbl.cell(0, 2).unwrap().set_text("Recycled %");
1577        tbl.cell(0, 2).unwrap().shading("6B8E23");
1578        let rows = [
1579            ("Server Hardware", "2,450", "94%"),
1580            ("Networking Equipment", "820", "91%"),
1581            ("Storage Media", "340", "99%"),
1582        ];
1583        for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1584            tbl.cell(i + 1, 0).unwrap().set_text(cat);
1585            tbl.cell(i + 1, 1).unwrap().set_text(wt);
1586            tbl.cell(i + 1, 2).unwrap().set_text(pct);
1587        }
1588    }
1589
1590    // ── Section 4: Initiatives ──
1591    doc.add_paragraph("").page_break_before(true);
1592    doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1593
1594    doc.add_paragraph("Planned Programs").style("Heading2");
1595    doc.add_numbered_list_item(
1596        "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1597        0,
1598    );
1599    doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1600    doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1601    doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1602
1603    doc.add_paragraph("Investment Targets").style("Heading2");
1604    doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1605        .keep_with_next(true);
1606    {
1607        let mut tbl = doc
1608            .add_table(5, 2)
1609            .borders(BorderStyle::Single, 4, "2D5016");
1610        tbl.cell(0, 0).unwrap().set_text("Initiative");
1611        tbl.cell(0, 0).unwrap().shading("2D5016");
1612        tbl.cell(0, 1).unwrap().set_text("Budget");
1613        tbl.cell(0, 1).unwrap().shading("2D5016");
1614        let rows = [
1615            ("Battery Storage", "$1.2M"),
1616            ("Immersion Cooling Pilot", "$800K"),
1617            ("Solar Panel Expansion", "$2.1M"),
1618            ("Carbon Credits", "$500K"),
1619        ];
1620        for (i, (init, budget)) in rows.iter().enumerate() {
1621            tbl.cell(i + 1, 0).unwrap().set_text(init);
1622            tbl.cell(i + 1, 1).unwrap().set_text(budget);
1623            if i % 2 == 0 {
1624                tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1625                tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1626            }
1627        }
1628    }
1629
1630    // Image: sustainability roadmap
1631    let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1632    doc.add_paragraph("");
1633    doc.add_picture(
1634        &roadmap_img,
1635        "roadmap.png",
1636        Length::inches(6.0),
1637        Length::inches(1.2),
1638    )
1639    .alignment(Alignment::Center);
1640    {
1641        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1642        p.add_run("Figure 2: 2026 Sustainability Roadmap")
1643            .italic(true)
1644            .size(9.0)
1645            .color("666666");
1646    }
1647
1648    doc.add_paragraph("");
1649    doc.add_paragraph(
1650        "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1651    )
1652    .shading("FFFAF0")
1653    .border_all(BorderStyle::Single, 2, "2D5016");
1654
1655    doc
1656}
Source

pub fn set_shading(&mut self, fill_color: &str)

Set a background/shading fill color in place.

Source

pub fn border_all( self, style: BorderStyle, size_eighths_pt: u32, color: &str, ) -> Self

Add a border to all sides.

Examples found in repository?
examples/generate_all_samples.rs (line 273)
97fn generate_feature_showcase(_samples_dir: &Path) -> Document {
98    let mut doc = Document::new();
99
100    // ── Page Setup & Metadata ──
101    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
102    doc.set_margins(
103        Length::inches(1.0),
104        Length::inches(1.0),
105        Length::inches(1.0),
106        Length::inches(1.0),
107    );
108    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
109    doc.set_gutter(Length::twips(0));
110    doc.set_title("rdocx Feature Showcase");
111    doc.set_author("rdocx Sample Generator");
112    doc.set_subject("Comprehensive feature demonstration");
113    doc.set_keywords("rdocx, docx, rust, sample, showcase");
114
115    // Headers & Footers with different first page
116    doc.set_different_first_page(true);
117    doc.set_first_page_header("rdocx");
118    doc.set_first_page_footer("Feature Showcase — Cover Page");
119    doc.set_header("rdocx Feature Showcase");
120    doc.set_footer("Generated by rdocx");
121
122    // ── COVER PAGE ──
123    let bg = create_sample_png(612, 792, [20, 45, 90]);
124    doc.add_background_image(&bg, "cover_bg.png");
125
126    for _ in 0..3 {
127        doc.add_paragraph("");
128    }
129    {
130        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
131        p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
132    }
133    {
134        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
135        p.add_run("Complete Feature Showcase")
136            .size(28.0)
137            .color("FFFFFF")
138            .italic(true);
139    }
140    doc.add_paragraph("");
141    {
142        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
143        p.add_run("Every feature of the rdocx Rust library")
144            .size(13.0)
145            .color("B0C4DE");
146    }
147    {
148        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
149        p.add_run("demonstrated in a single document.")
150            .size(13.0)
151            .color("B0C4DE");
152    }
153
154    // ── TABLE OF CONTENTS ──
155    doc.add_paragraph("").page_break_before(true);
156    doc.insert_toc(doc.content_count(), 3);
157
158    // ── SECTION 1: TEXT FORMATTING ──
159    doc.add_paragraph("").page_break_before(true);
160    doc.add_paragraph("1. Text Formatting").style("Heading1");
161
162    // Paragraph Alignment
163    doc.add_paragraph("1.1 Paragraph Alignment")
164        .style("Heading2");
165    doc.add_paragraph("Left-aligned paragraph (default).")
166        .alignment(Alignment::Left);
167    doc.add_paragraph("Center-aligned paragraph.")
168        .alignment(Alignment::Center);
169    doc.add_paragraph("Right-aligned paragraph.")
170        .alignment(Alignment::Right);
171    doc.add_paragraph(
172        "Justified paragraph — this text is long enough to demonstrate how justified alignment \
173         distributes extra space across word gaps so lines fill the full width of the text area.",
174    )
175    .alignment(Alignment::Justify);
176
177    doc.add_paragraph("");
178
179    // Run Formatting
180    doc.add_paragraph("1.2 Run Formatting").style("Heading2");
181    {
182        let mut p = doc.add_paragraph("");
183        p.add_run("Bold").bold(true);
184        p.add_run(" | ");
185        p.add_run("Italic").italic(true);
186        p.add_run(" | ");
187        p.add_run("Bold Italic").bold(true).italic(true);
188        p.add_run(" | ");
189        p.add_run("Underline").underline(true);
190        p.add_run(" | ");
191        p.add_run("Strikethrough").strike(true);
192        p.add_run(" | ");
193        p.add_run("Double Strike").double_strike(true);
194    }
195    {
196        let mut p = doc.add_paragraph("");
197        p.add_run("Red").color("FF0000");
198        p.add_run(" | ");
199        p.add_run("Blue").color("0000FF");
200        p.add_run(" | ");
201        p.add_run("Green").color("00AA00");
202        p.add_run(" | ");
203        p.add_run("Highlighted").highlight("FFFF00");
204    }
205    {
206        let mut p = doc.add_paragraph("");
207        p.add_run("8pt").size(8.0);
208        p.add_run(" | ");
209        p.add_run("11pt").size(11.0);
210        p.add_run(" | ");
211        p.add_run("16pt").size(16.0);
212        p.add_run(" | ");
213        p.add_run("24pt").size(24.0);
214    }
215    {
216        let mut p = doc.add_paragraph("");
217        p.add_run("Arial").font("Arial");
218        p.add_run(" | ");
219        p.add_run("Times New Roman").font("Times New Roman");
220        p.add_run(" | ");
221        p.add_run("Courier New").font("Courier New");
222    }
223    {
224        let mut p = doc.add_paragraph("");
225        p.add_run("H");
226        p.add_run("2").subscript();
227        p.add_run("O (subscript) | E=mc");
228        p.add_run("2").superscript();
229        p.add_run(" (superscript)");
230    }
231    {
232        let mut p = doc.add_paragraph("");
233        p.add_run("ALL CAPS").all_caps(true);
234        p.add_run(" | ");
235        p.add_run("Small Caps").small_caps(true);
236        p.add_run(" | ");
237        p.add_run("Expanded").character_spacing(Length::twips(40));
238        p.add_run(" | ");
239        p.add_run("Hidden text (not visible)").hidden(true);
240    }
241
242    doc.add_paragraph("");
243
244    // Underline Styles
245    doc.add_paragraph("1.3 Underline Styles").style("Heading2");
246    {
247        let mut p = doc.add_paragraph("");
248        p.add_run("Single ")
249            .underline_style(rdocx::UnderlineStyle::Single);
250        p.add_run("Double ")
251            .underline_style(rdocx::UnderlineStyle::Double);
252        p.add_run("Thick ")
253            .underline_style(rdocx::UnderlineStyle::Thick);
254        p.add_run("Dotted ")
255            .underline_style(rdocx::UnderlineStyle::Dotted);
256        p.add_run("Dash ")
257            .underline_style(rdocx::UnderlineStyle::Dash);
258        p.add_run("Wave ")
259            .underline_style(rdocx::UnderlineStyle::Wave);
260    }
261
262    // ── SECTION 2: PARAGRAPH FORMATTING ──
263    doc.add_paragraph("").page_break_before(true);
264    doc.add_paragraph("2. Paragraph Formatting")
265        .style("Heading1");
266
267    doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
268    doc.add_paragraph("Paragraph with light green shading")
269        .shading("E2EFDA");
270    doc.add_paragraph("Paragraph with bottom border")
271        .border_bottom(BorderStyle::Single, 6, "2E75B6");
272    doc.add_paragraph("Paragraph with all borders (red)")
273        .border_all(BorderStyle::Single, 4, "FF0000");
274
275    doc.add_paragraph("2.2 Indentation").style("Heading2");
276    doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
277        .indent_left(Length::inches(1.0))
278        .hanging_indent(Length::inches(0.5));
279    doc.add_paragraph("0.5-inch first-line indent on this paragraph")
280        .first_line_indent(Length::inches(0.5));
281    doc.add_paragraph("Both left and right indent (0.75 inches each)")
282        .indent_left(Length::inches(0.75))
283        .indent_right(Length::inches(0.75));
284
285    doc.add_paragraph("2.3 Spacing & Line Height")
286        .style("Heading2");
287    doc.add_paragraph("Extra space before (24pt) and after (12pt)")
288        .space_before(Length::pt(24.0))
289        .space_after(Length::pt(12.0));
290    doc.add_paragraph(
291        "Double line spacing paragraph. This text should have extra vertical space between \
292         lines to demonstrate line_spacing_multiple(2.0).",
293    )
294    .line_spacing_multiple(2.0);
295    doc.add_paragraph("Exact 20pt line spacing (fixed height).")
296        .line_spacing(20.0);
297
298    doc.add_paragraph("2.4 Pagination Controls")
299        .style("Heading2");
300    doc.add_paragraph("keep_with_next — this paragraph stays with the next")
301        .keep_with_next(true);
302    doc.add_paragraph("(This paragraph was kept with the one above.)");
303    doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
304        .keep_together(true);
305    doc.add_paragraph("widow_control — prevents widow/orphan lines")
306        .widow_control(true);
307
308    // ── SECTION 3: LISTS ──
309    doc.add_paragraph("").page_break_before(true);
310    doc.add_paragraph("3. Lists").style("Heading1");
311
312    doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
313    doc.add_bullet_list_item("First item", 0);
314    doc.add_bullet_list_item("Second item", 0);
315    doc.add_bullet_list_item("Nested level 1", 1);
316    doc.add_bullet_list_item("Nested level 2", 2);
317    doc.add_bullet_list_item("Back to level 1", 1);
318    doc.add_bullet_list_item("Third item", 0);
319
320    doc.add_paragraph("");
321
322    doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
323    doc.add_numbered_list_item("First numbered item", 0);
324    doc.add_numbered_list_item("Second numbered item", 0);
325    doc.add_numbered_list_item("Sub-item A", 1);
326    doc.add_numbered_list_item("Sub-item B", 1);
327    doc.add_numbered_list_item("Third numbered item", 0);
328
329    // ── SECTION 4: TAB STOPS ──
330    doc.add_paragraph("");
331    doc.add_paragraph("4. Tab Stops").style("Heading1");
332
333    doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
334    doc.add_paragraph("Left\tCenter\tRight\tDecimal")
335        .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
336        .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
337        .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
338        .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
339
340    doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
341    doc.add_paragraph("Item\t\tPrice")
342        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
343        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
344        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
345    doc.add_paragraph("Widget\t\t$19.99")
346        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
347        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
348        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
349    doc.add_paragraph("Gadget\t\t$249.50")
350        .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
351        .add_tab_stop_with_leader(
352            TabAlignment::Right,
353            Length::inches(4.0),
354            TabLeader::Underscore,
355        )
356        .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
357
358    // ── SECTION 5: TABLES ──
359    doc.add_paragraph("").page_break_before(true);
360    doc.add_paragraph("5. Tables").style("Heading1");
361
362    doc.add_paragraph("5.1 Basic Table").style("Heading2");
363    {
364        let mut tbl = doc
365            .add_table(4, 3)
366            .borders(BorderStyle::Single, 4, "000000");
367        for col in 0..3 {
368            tbl.cell(0, col).unwrap().shading("2E75B6");
369        }
370        tbl.cell(0, 0).unwrap().set_text("Name");
371        tbl.cell(0, 1).unwrap().set_text("Role");
372        tbl.cell(0, 2).unwrap().set_text("Location");
373        tbl.cell(1, 0).unwrap().set_text("Walter White");
374        tbl.cell(1, 1).unwrap().set_text("CEO");
375        tbl.cell(1, 2).unwrap().set_text("Albuquerque");
376        tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
377        tbl.cell(2, 1).unwrap().set_text("CTO");
378        tbl.cell(2, 2).unwrap().set_text("Remote");
379        tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
380        tbl.cell(3, 1).unwrap().set_text("Security");
381        tbl.cell(3, 2).unwrap().set_text("Washington");
382    }
383
384    doc.add_paragraph("");
385
386    doc.add_paragraph("5.2 Column Span & Cell Shading")
387        .style("Heading2");
388    {
389        let mut tbl = doc
390            .add_table(3, 4)
391            .borders(BorderStyle::Single, 4, "000000")
392            .width_pct(100.0);
393        tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
394        tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
395        tbl.cell(1, 0).unwrap().set_text("Region");
396        tbl.cell(1, 0).unwrap().shading("D6E4F0");
397        tbl.cell(1, 1).unwrap().set_text("Q1");
398        tbl.cell(1, 1).unwrap().shading("D6E4F0");
399        tbl.cell(1, 2).unwrap().set_text("Q2");
400        tbl.cell(1, 2).unwrap().shading("D6E4F0");
401        tbl.cell(1, 3).unwrap().set_text("Total");
402        tbl.cell(1, 3).unwrap().shading("D6E4F0");
403        tbl.cell(2, 0).unwrap().set_text("Americas");
404        tbl.cell(2, 1).unwrap().set_text("$2.4M");
405        tbl.cell(2, 2).unwrap().set_text("$2.7M");
406        tbl.cell(2, 3).unwrap().set_text("$5.1M");
407    }
408
409    doc.add_paragraph("");
410
411    doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
412    {
413        let mut tbl = doc
414            .add_table(4, 3)
415            .borders(BorderStyle::Single, 4, "000000");
416        tbl.cell(0, 0).unwrap().set_text("Category");
417        tbl.cell(0, 0).unwrap().shading("E2EFDA");
418        tbl.cell(0, 1).unwrap().set_text("Item");
419        tbl.cell(0, 1).unwrap().shading("E2EFDA");
420        tbl.cell(0, 2).unwrap().set_text("Price");
421        tbl.cell(0, 2).unwrap().shading("E2EFDA");
422        tbl.cell(1, 0).unwrap().set_text("Hardware");
423        tbl.cell(1, 0).unwrap().v_merge_restart();
424        tbl.cell(1, 1).unwrap().set_text("Laptop");
425        tbl.cell(1, 2).unwrap().set_text("$1,200");
426        tbl.cell(2, 0).unwrap().v_merge_continue();
427        tbl.cell(2, 1).unwrap().set_text("Monitor");
428        tbl.cell(2, 2).unwrap().set_text("$450");
429        tbl.cell(3, 0).unwrap().set_text("Software");
430        tbl.cell(3, 1).unwrap().set_text("IDE License");
431        tbl.cell(3, 2).unwrap().set_text("$200/yr");
432    }
433
434    doc.add_paragraph("");
435
436    doc.add_paragraph("5.4 Nested Table").style("Heading2");
437    {
438        let mut tbl = doc
439            .add_table(2, 2)
440            .borders(BorderStyle::Single, 6, "2E75B6");
441        tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
442        tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
443        tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
444        {
445            let mut cell = tbl.cell(1, 1).unwrap();
446            cell.set_text("Nested table below:");
447            let mut nested = cell
448                .add_table(2, 2)
449                .borders(BorderStyle::Single, 2, "FF6600");
450            nested.cell(0, 0).unwrap().set_text("A");
451            nested.cell(0, 1).unwrap().set_text("B");
452            nested.cell(1, 0).unwrap().set_text("C");
453            nested.cell(1, 1).unwrap().set_text("D");
454        }
455    }
456
457    doc.add_paragraph("");
458
459    doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
460        .style("Heading2");
461    {
462        let mut tbl = doc
463            .add_table(2, 3)
464            .borders(BorderStyle::Single, 4, "666666");
465        tbl.row(0).unwrap().height(Length::pt(50.0)).header();
466        tbl.cell(0, 0).unwrap().set_text("Top");
467        tbl.cell(0, 0)
468            .unwrap()
469            .vertical_alignment(VerticalAlignment::Top)
470            .shading("FFF2CC");
471        tbl.cell(0, 1).unwrap().set_text("Center");
472        tbl.cell(0, 1)
473            .unwrap()
474            .vertical_alignment(VerticalAlignment::Center)
475            .shading("D9E2F3");
476        tbl.cell(0, 2).unwrap().set_text("Bottom");
477        tbl.cell(0, 2)
478            .unwrap()
479            .vertical_alignment(VerticalAlignment::Bottom)
480            .shading("E2EFDA");
481        tbl.row(1).unwrap().cant_split();
482        tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
483        tbl.cell(1, 0).unwrap().no_wrap();
484        tbl.cell(1, 1).unwrap().set_text("Fixed width");
485        tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
486        tbl.cell(1, 2).unwrap().set_text("Normal");
487    }
488
489    // ── SECTION 6: IMAGES ──
490    doc.add_paragraph("").page_break_before(true);
491    doc.add_paragraph("6. Images").style("Heading1");
492
493    doc.add_paragraph("6.1 Inline Image").style("Heading2");
494    doc.add_paragraph("A blue gradient image below:");
495    let img = create_sample_png(200, 50, [0, 80, 200]);
496    doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
497
498    doc.add_paragraph("");
499    doc.add_paragraph("6.2 Header Image").style("Heading2");
500    let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
501    doc.set_header_image(
502        &hdr_img,
503        "header_logo.png",
504        Length::inches(2.0),
505        Length::inches(0.2),
506    );
507    doc.add_paragraph("The header now contains an inline image (check the top of this page).");
508
509    doc.add_paragraph("");
510    doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
511
512    // ── SECTION 7: CONTENT MANIPULATION ──
513    doc.add_paragraph("").page_break_before(true);
514    doc.add_paragraph("7. Content Manipulation")
515        .style("Heading1");
516
517    doc.add_paragraph("7.1 Placeholder Replacement")
518        .style("Heading2");
519    doc.add_paragraph("Customer: {{customer}}");
520    doc.add_paragraph("Date: {{date}}");
521    doc.add_paragraph("Reference: {{ref_number}}");
522    {
523        let mut tbl = doc
524            .add_table(2, 2)
525            .borders(BorderStyle::Single, 4, "000000");
526        tbl.cell(0, 0).unwrap().set_text("Project");
527        tbl.cell(0, 0).unwrap().shading("D6E4F0");
528        tbl.cell(0, 1).unwrap().set_text("{{project}}");
529        tbl.cell(1, 0).unwrap().set_text("Status");
530        tbl.cell(1, 0).unwrap().shading("D6E4F0");
531        tbl.cell(1, 1).unwrap().set_text("{{status}}");
532    }
533
534    let mut replacements = HashMap::new();
535    replacements.insert("{{customer}}", "Tensorbee Inc.");
536    replacements.insert("{{date}}", "February 22, 2026");
537    replacements.insert("{{ref_number}}", "TB-2026-001");
538    replacements.insert("{{project}}", "Infrastructure Upgrade");
539    replacements.insert("{{status}}", "In Progress");
540    let n = doc.replace_all(&replacements);
541    doc.add_paragraph(&format!("({n} placeholders replaced above)"));
542
543    doc.add_paragraph("");
544
545    doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
546    doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
547    let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
548    doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
549
550    doc.add_paragraph("");
551
552    doc.add_paragraph("7.3 Content Insertion").style("Heading2");
553    doc.add_paragraph("Section A: First content.");
554    doc.add_paragraph("Section C: Third content.");
555    if let Some(idx) = doc.find_content_index("Section C") {
556        doc.insert_paragraph(
557            idx,
558            "Section B: Inserted between A and C via find_content_index().",
559        );
560    }
561
562    // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
563    doc.add_paragraph("").section_break(SectionBreak::NextPage);
564    doc.add_paragraph("8. Section Breaks & Orientation")
565        .style("Heading1");
566    doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
567
568    {
569        let mut tbl = doc
570            .add_table(3, 7)
571            .borders(BorderStyle::Single, 4, "2E75B6");
572        let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
573        for (c, h) in headers.iter().enumerate() {
574            tbl.cell(0, c).unwrap().set_text(h);
575            tbl.cell(0, c).unwrap().shading("2E75B6");
576        }
577        let data = [
578            [
579                "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
580            ],
581            [
582                "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
583            ],
584        ];
585        for (r, row) in data.iter().enumerate() {
586            for (c, v) in row.iter().enumerate() {
587                tbl.cell(r + 1, c).unwrap().set_text(v);
588            }
589        }
590    }
591
592    doc.add_paragraph("")
593        .section_break(SectionBreak::NextPage)
594        .section_landscape();
595
596    // ── SECTION 9: CUSTOM STYLES ──
597    doc.add_paragraph("9. Custom Styles").style("Heading1");
598    doc.add_style(
599        StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
600            .based_on("Normal")
601            .paragraph_properties(rdocx_oxml::properties::CT_PPr {
602                shading: Some(rdocx_oxml::properties::CT_Shd {
603                    val: "clear".to_string(),
604                    color: None,
605                    fill: Some("FFF2CC".to_string()),
606                }),
607                ..Default::default()
608            })
609            .run_properties(rdocx_oxml::properties::CT_RPr {
610                bold: Some(true),
611                color: Some("C45911".to_string()),
612                ..Default::default()
613            }),
614    );
615    doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
616        .style("CustomHighlight");
617
618    doc.add_paragraph("");
619
620    // ── SECTION 10: DOCUMENT INTELLIGENCE ──
621    doc.add_paragraph("10. Document Intelligence API")
622        .style("Heading1");
623
624    let wc = doc.word_count();
625    let hc = doc.headings().len();
626    let ic = doc.images().len();
627    let lc = doc.links().len();
628    doc.add_paragraph(&format!("Word count: {wc}"));
629    doc.add_paragraph(&format!("Heading count: {hc}"));
630    doc.add_paragraph(&format!("Image count: {ic}"));
631    doc.add_paragraph(&format!("Link count: {lc}"));
632
633    let outline = doc.document_outline();
634    doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
635
636    let issues = doc.audit_accessibility();
637    doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
638    for issue in &issues {
639        doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
640    }
641
642    // ── SECTION 11: DOCUMENT MERGING ──
643    doc.add_paragraph("");
644    doc.add_paragraph("11. Document Merging").style("Heading1");
645    {
646        let mut other = Document::new();
647        other.add_paragraph("This paragraph was merged from another document using append().");
648        doc.append(&other);
649    }
650
651    doc.add_paragraph("");
652
653    // ── SUMMARY ──
654    doc.add_paragraph("Summary of Demonstrated Features")
655        .style("Heading1");
656    let features = [
657        "Page setup: size, margins, header/footer distance, gutter",
658        "Document metadata: title, author, subject, keywords",
659        "Headers and footers: text, images, different first page",
660        "Background images (full-page behind text)",
661        "Table of Contents generation with bookmarks",
662        "Text formatting: bold, italic, underline styles, strike, color, size, font",
663        "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
664        "Paragraph formatting: alignment, borders, shading, spacing, indentation",
665        "Pagination controls: keep-with-next, keep-together, widow control",
666        "Bullet and numbered lists with nesting",
667        "Tab stops with dot/underscore/hyphen leaders",
668        "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
669        "Row properties: header rows, exact height, cant-split",
670        "Cell properties: width, no-wrap, vertical alignment",
671        "Inline images and header images",
672        "Placeholder replacement (single and batch)",
673        "Regex-based find and replace",
674        "Content insertion at specific positions",
675        "Section breaks with mixed portrait/landscape",
676        "Custom paragraph and character styles",
677        "Document intelligence: word count, headings, outline, images, links",
678        "Accessibility audit",
679        "Document merging (append)",
680        "Export: DOCX, PDF, HTML, Markdown",
681    ];
682    for f in &features {
683        doc.add_bullet_list_item(f, 0);
684    }
685    doc.add_paragraph("");
686    doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
687        .alignment(Alignment::Center)
688        .shading("E2EFDA")
689        .border_all(BorderStyle::Single, 2, "00AA00");
690
691    doc
692}
693
694// =============================================================================
695// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
696// =============================================================================
697fn generate_proposal(_samples_dir: &Path) -> Document {
698    let mut doc = Document::new();
699
700    // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
701    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
702    doc.set_margins(
703        Length::inches(1.0),
704        Length::inches(1.0),
705        Length::inches(1.0),
706        Length::inches(1.0),
707    );
708    doc.set_title("AI Platform Modernization Proposal");
709    doc.set_author("Walter White");
710    doc.set_subject("Technology Proposal");
711    doc.set_keywords("proposal, AI, modernization, Tensorbee");
712
713    // Banner header
714    let logo_img = create_logo_png(220, 48);
715    let banner = build_header_banner_xml(
716        "rId1",
717        &BannerOpts {
718            bg_color: "1B2A4A",
719            banner_width: 7772400,
720            banner_height: 914400,
721            logo_width: 2011680,
722            logo_height: 438912,
723            logo_x_offset: 295125,
724            logo_y_offset: 237744,
725        },
726    );
727    doc.set_raw_header_with_images(
728        banner,
729        &[("rId1", &logo_img, "logo.png")],
730        rdocx_oxml::header_footer::HdrFtrType::Default,
731    );
732    doc.set_different_first_page(true);
733    let cover_banner = build_header_banner_xml(
734        "rId1",
735        &BannerOpts {
736            bg_color: "C5922E",
737            banner_width: 7772400,
738            banner_height: 914400,
739            logo_width: 2011680,
740            logo_height: 438912,
741            logo_x_offset: 295125,
742            logo_y_offset: 237744,
743        },
744    );
745    doc.set_raw_header_with_images(
746        cover_banner,
747        &[("rId1", &logo_img, "logo.png")],
748        rdocx_oxml::header_footer::HdrFtrType::First,
749    );
750    doc.set_margins(
751        Length::twips(2292),
752        Length::twips(1440),
753        Length::twips(1440),
754        Length::twips(1440),
755    );
756    doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
757    doc.set_footer("Tensorbee — Confidential");
758
759    // Custom styles
760    doc.add_style(
761        StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
762            .based_on("Normal")
763            .run_properties(rdocx_oxml::properties::CT_RPr {
764                bold: Some(true),
765                font_ascii: Some("Georgia".to_string()),
766                font_hansi: Some("Georgia".to_string()),
767                sz: Some(rdocx_oxml::HalfPoint(56)), // half-points → 28pt
768                color: Some("1B2A4A".to_string()),
769                ..Default::default()
770            }),
771    );
772
773    // ── Cover Page ──
774    for _ in 0..4 {
775        doc.add_paragraph("");
776    }
777    doc.add_paragraph("AI Platform Modernization")
778        .style("ProposalTitle")
779        .alignment(Alignment::Center);
780    doc.add_paragraph("")
781        .alignment(Alignment::Center)
782        .border_bottom(BorderStyle::Single, 8, "C5922E");
783    {
784        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
785        p.add_run("Prepared for Global Financial Corp.")
786            .size(14.0)
787            .color("666666")
788            .italic(true);
789    }
790    {
791        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
792        p.add_run("Prepared by: Walter White, CEO — Tensorbee")
793            .size(12.0)
794            .color("888888");
795    }
796    {
797        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
798        p.add_run("February 22, 2026").size(12.0).color("888888");
799    }
800
801    // ── TOC ──
802    doc.add_paragraph("").page_break_before(true);
803    doc.add_paragraph("Table of Contents").style("Heading1");
804    // We'll add a manual-style TOC since the insert_toc goes at a specific position
805    doc.insert_toc(doc.content_count(), 2);
806
807    // ── Executive Summary ──
808    doc.add_paragraph("").page_break_before(true);
809    doc.add_paragraph("Executive Summary").style("Heading1");
810    doc.add_paragraph(
811        "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
812         transitioning from legacy batch-processing systems to a real-time inference architecture. \
813         This transformation will reduce model deployment time from weeks to hours, improve \
814         prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
815    )
816    .first_line_indent(Length::inches(0.3));
817    doc.add_paragraph(
818        "The project spans three phases over 18 months, with a total investment of $2.8M. \
819         Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
820         similar transformations for three Fortune 500 financial institutions.",
821    )
822    .first_line_indent(Length::inches(0.3));
823
824    // Key metrics table
825    doc.add_paragraph("");
826    {
827        let mut tbl = doc
828            .add_table(5, 2)
829            .borders(BorderStyle::Single, 4, "1B2A4A")
830            .width_pct(80.0);
831        tbl.cell(0, 0).unwrap().set_text("Key Metric");
832        tbl.cell(0, 0).unwrap().shading("1B2A4A");
833        tbl.cell(0, 1).unwrap().set_text("Value");
834        tbl.cell(0, 1).unwrap().shading("1B2A4A");
835        let rows = [
836            ("Total Investment", "$2.8M"),
837            ("Annual Savings", "$4.2M"),
838            ("ROI (Year 1)", "150%"),
839            ("Timeline", "18 months"),
840        ];
841        for (i, (k, v)) in rows.iter().enumerate() {
842            tbl.cell(i + 1, 0).unwrap().set_text(k);
843            if i % 2 == 0 {
844                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
845            }
846            tbl.cell(i + 1, 1).unwrap().set_text(v);
847            if i % 2 == 0 {
848                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
849            }
850        }
851    }
852
853    // ── Problem Statement ──
854    doc.add_paragraph("").page_break_before(true);
855    doc.add_paragraph("Problem Statement").style("Heading1");
856    doc.add_paragraph(
857        "Global Financial Corp's current AI infrastructure faces three critical challenges:",
858    );
859    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);
860    doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
861    doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
862
863    // ── Proposed Solution ──
864    doc.add_paragraph("").page_break_before(true);
865    doc.add_paragraph("Proposed Solution").style("Heading1");
866
867    doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
868        .style("Heading2");
869    doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
870    doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
871    doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
872
873    doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
874        .style("Heading2");
875    doc.add_bullet_list_item(
876        "Real-time feature engineering pipeline (Apache Kafka + Flink)",
877        0,
878    );
879    doc.add_bullet_list_item("A/B testing framework for model variants", 0);
880    doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
881
882    doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
883        .style("Heading2");
884    doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
885    doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
886    doc.add_bullet_list_item(
887        "Self-service model deployment portal for data science teams",
888        0,
889    );
890
891    // ── Budget ──
892    doc.add_paragraph("Budget Breakdown").style("Heading1");
893    {
894        let mut tbl = doc
895            .add_table(6, 3)
896            .borders(BorderStyle::Single, 4, "1B2A4A")
897            .width_pct(100.0);
898        tbl.cell(0, 0).unwrap().set_text("Category");
899        tbl.cell(0, 0).unwrap().shading("1B2A4A");
900        tbl.cell(0, 1).unwrap().set_text("Cost");
901        tbl.cell(0, 1).unwrap().shading("1B2A4A");
902        tbl.cell(0, 2).unwrap().set_text("Notes");
903        tbl.cell(0, 2).unwrap().shading("1B2A4A");
904        let items = [
905            (
906                "Infrastructure",
907                "$450,000",
908                "Cloud compute + GPU instances",
909            ),
910            ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
911            (
912                "Software Licenses",
913                "$350,000",
914                "Kafka, monitoring, CI/CD tools",
915            ),
916            (
917                "Training & Enablement",
918                "$200,000",
919                "Team training, documentation",
920            ),
921            ("Contingency (10%)", "$200,000", "Risk buffer"),
922        ];
923        for (i, (cat, cost, note)) in items.iter().enumerate() {
924            tbl.cell(i + 1, 0).unwrap().set_text(cat);
925            tbl.cell(i + 1, 1).unwrap().set_text(cost);
926            tbl.cell(i + 1, 2).unwrap().set_text(note);
927            if i % 2 == 0 {
928                tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
929                tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
930                tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
931            }
932        }
933    }
934
935    // ── Closing ──
936    doc.add_paragraph("");
937    doc.add_paragraph("Next Steps").style("Heading1");
938    doc.add_numbered_list_item(
939        "Schedule technical deep-dive with GFC engineering team (Week 1)",
940        0,
941    );
942    doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
943    doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
944
945    doc.add_paragraph("");
946    {
947        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
948        p.add_run("Walter White")
949            .bold(true)
950            .size(14.0)
951            .color("1B2A4A");
952    }
953    {
954        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
955        p.add_run("CEO, Tensorbee | walter@tensorbee.com")
956            .size(10.0)
957            .color("888888");
958    }
959
960    doc
961}
962
963// =============================================================================
964// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
965// =============================================================================
966fn generate_quote(_samples_dir: &Path) -> Document {
967    let mut doc = Document::new();
968
969    // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
970    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
971    doc.set_margins(
972        Length::inches(0.75),
973        Length::inches(0.75),
974        Length::inches(0.75),
975        Length::inches(0.75),
976    );
977    doc.set_title("Quotation QT-2026-0147");
978    doc.set_author("Walter White");
979    doc.set_keywords("quote, BOM, Tensorbee");
980
981    doc.set_header("Tensorbee — Quotation");
982    doc.set_footer("QT-2026-0147 | Page");
983
984    // ── Header Block ──
985    {
986        let mut p = doc.add_paragraph("").alignment(Alignment::Left);
987        p.add_run("TENSORBEE")
988            .bold(true)
989            .size(28.0)
990            .color("008B8B")
991            .font("Helvetica");
992    }
993    doc.add_paragraph("123 Innovation Drive, Suite 400")
994        .alignment(Alignment::Left);
995    doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
996        .alignment(Alignment::Left);
997    doc.add_paragraph("")
998        .border_bottom(BorderStyle::Single, 8, "008B8B");
999
1000    // Quote meta
1001    doc.add_paragraph("");
1002    {
1003        let mut tbl = doc.add_table(4, 4).width_pct(100.0);
1004        tbl.cell(0, 0).unwrap().set_text("QUOTATION");
1005        tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
1006        tbl.cell(0, 2).unwrap().set_text("DATE");
1007        tbl.cell(0, 2).unwrap().shading("008B8B");
1008        tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1009        tbl.cell(0, 3).unwrap().shading("008B8B");
1010        tbl.cell(1, 0).unwrap().set_text("Quote #:");
1011        tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1012        tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1013        tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1014        tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1015        tbl.cell(2, 1).unwrap().set_text("Walter White");
1016        tbl.cell(2, 2).unwrap().set_text("Payment:");
1017        tbl.cell(2, 3).unwrap().set_text("Net 30");
1018        tbl.cell(3, 0).unwrap().set_text("Customer:");
1019        tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1020        tbl.cell(3, 2).unwrap().set_text("Currency:");
1021        tbl.cell(3, 3).unwrap().set_text("USD");
1022    }
1023
1024    doc.add_paragraph("");
1025
1026    // ── Bill of Materials ──
1027    {
1028        let mut p = doc.add_paragraph("");
1029        p.add_run("Bill of Materials")
1030            .bold(true)
1031            .size(16.0)
1032            .color("1A3C3C");
1033    }
1034    doc.add_paragraph("");
1035
1036    {
1037        let mut tbl = doc
1038            .add_table(10, 6)
1039            .borders(BorderStyle::Single, 2, "008B8B")
1040            .width_pct(100.0)
1041            .layout_fixed();
1042        // Headers
1043        let hdrs = [
1044            "#",
1045            "Part Number",
1046            "Description",
1047            "Qty",
1048            "Unit Price",
1049            "Total",
1050        ];
1051        for (c, h) in hdrs.iter().enumerate() {
1052            tbl.cell(0, c).unwrap().set_text(h);
1053            tbl.cell(0, c).unwrap().shading("008B8B");
1054        }
1055
1056        let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1057            (
1058                "TB-GPU-A100",
1059                "NVIDIA A100 80GB GPU",
1060                "4",
1061                "$12,500.00",
1062                "$50,000.00",
1063            ),
1064            (
1065                "TB-SRV-R750",
1066                "Dell R750xa Server Chassis",
1067                "2",
1068                "$8,200.00",
1069                "$16,400.00",
1070            ),
1071            (
1072                "TB-RAM-256G",
1073                "256GB DDR5 ECC Memory Module",
1074                "8",
1075                "$890.00",
1076                "$7,120.00",
1077            ),
1078            (
1079                "TB-SSD-3840",
1080                "3.84TB NVMe U.2 SSD",
1081                "8",
1082                "$1,150.00",
1083                "$9,200.00",
1084            ),
1085            (
1086                "TB-NET-CX7",
1087                "ConnectX-7 200GbE NIC",
1088                "4",
1089                "$1,800.00",
1090                "$7,200.00",
1091            ),
1092            (
1093                "TB-SW-48P",
1094                "48-Port 100GbE Switch",
1095                "1",
1096                "$22,000.00",
1097                "$22,000.00",
1098            ),
1099            (
1100                "TB-CAB-RACK",
1101                "42U Server Rack + PDU",
1102                "1",
1103                "$4,500.00",
1104                "$4,500.00",
1105            ),
1106            (
1107                "TB-SVC-INST",
1108                "Installation & Configuration",
1109                "1",
1110                "$8,500.00",
1111                "$8,500.00",
1112            ),
1113            (
1114                "TB-SVC-SUPP",
1115                "3-Year Premium Support",
1116                "1",
1117                "$15,000.00",
1118                "$15,000.00",
1119            ),
1120        ];
1121
1122        for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1123            let row = i + 1;
1124            tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1125            tbl.cell(row, 1).unwrap().set_text(pn);
1126            tbl.cell(row, 2).unwrap().set_text(desc);
1127            tbl.cell(row, 3).unwrap().set_text(qty);
1128            tbl.cell(row, 4).unwrap().set_text(unit);
1129            tbl.cell(row, 5).unwrap().set_text(total);
1130            if i % 2 == 0 {
1131                for c in 0..6 {
1132                    tbl.cell(row, c).unwrap().shading("F0F8F8");
1133                }
1134            }
1135        }
1136    }
1137
1138    doc.add_paragraph("");
1139
1140    // ── Totals ──
1141    {
1142        let mut tbl = doc
1143            .add_table(4, 2)
1144            .borders(BorderStyle::Single, 2, "008B8B")
1145            .width(Length::inches(3.5))
1146            .alignment(Alignment::Right);
1147        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1148        tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1149        tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1150        tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1151        tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1152        tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1153        tbl.cell(3, 0).unwrap().set_text("TOTAL");
1154        tbl.cell(3, 0).unwrap().shading("E07020");
1155        tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1156        tbl.cell(3, 1).unwrap().shading("E07020");
1157    }
1158
1159    doc.add_paragraph("");
1160
1161    // ── Terms & Conditions ──
1162    {
1163        let mut p = doc.add_paragraph("");
1164        p.add_run("Terms & Conditions")
1165            .bold(true)
1166            .size(14.0)
1167            .color("1A3C3C");
1168    }
1169    doc.add_numbered_list_item(
1170        "This quotation is valid for 30 calendar days from the date of issue.",
1171        0,
1172    );
1173    doc.add_numbered_list_item(
1174        "All prices are in USD and exclusive of applicable taxes unless stated.",
1175        0,
1176    );
1177    doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1178    doc.add_numbered_list_item(
1179        "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1180        0,
1181    );
1182    doc.add_numbered_list_item(
1183        "Warranty: 3-year manufacturer warranty on all hardware components.",
1184        0,
1185    );
1186    doc.add_numbered_list_item(
1187        "Returns subject to 15% restocking fee if initiated after 14 days.",
1188        0,
1189    );
1190
1191    doc.add_paragraph("");
1192    doc.add_paragraph("")
1193        .border_bottom(BorderStyle::Single, 4, "008B8B");
1194    doc.add_paragraph("");
1195    {
1196        let mut p = doc.add_paragraph("");
1197        p.add_run("Accepted by: ").bold(true);
1198        p.add_run("___________________________ Date: ___________");
1199    }
1200    {
1201        let mut p = doc.add_paragraph("");
1202        p.add_run("Print Name: ").bold(true);
1203        p.add_run("___________________________ Title: ___________");
1204    }
1205
1206    doc
1207}
1208
1209// =============================================================================
1210// 4. INVOICE — Crimson + charcoal scheme
1211// =============================================================================
1212fn generate_invoice(_samples_dir: &Path) -> Document {
1213    let mut doc = Document::new();
1214
1215    // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1216    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1217    doc.set_margins(
1218        Length::inches(0.75),
1219        Length::inches(0.75),
1220        Length::inches(0.75),
1221        Length::inches(0.75),
1222    );
1223    doc.set_title("Invoice INV-2026-0392");
1224    doc.set_author("Walter White");
1225    doc.set_keywords("invoice, Tensorbee");
1226
1227    doc.set_footer("Tensorbee — Thank you for your business!");
1228
1229    // ── Company & Invoice Header ──
1230    {
1231        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1232        // Company name left, INVOICE right
1233        {
1234            let mut cell = tbl.cell(0, 0).unwrap();
1235            let mut p = cell.add_paragraph("");
1236            p.add_run("TENSORBEE")
1237                .bold(true)
1238                .size(32.0)
1239                .color("B22222")
1240                .font("Helvetica");
1241        }
1242        {
1243            let mut cell = tbl.cell(0, 1).unwrap();
1244            let mut p = cell.add_paragraph("");
1245            p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1246        }
1247        tbl.cell(1, 0)
1248            .unwrap()
1249            .set_text("123 Innovation Drive, Suite 400");
1250        tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1251        tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1252        tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1253        tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1254        tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1255    }
1256
1257    doc.add_paragraph("")
1258        .border_bottom(BorderStyle::Thick, 8, "B22222");
1259    doc.add_paragraph("");
1260
1261    // ── Bill To / Ship To ──
1262    {
1263        let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1264        tbl.cell(0, 0).unwrap().set_text("BILL TO");
1265        tbl.cell(0, 0).unwrap().shading("B22222");
1266        tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1267        tbl.cell(0, 1).unwrap().shading("B22222");
1268        tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1269        tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1270        tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1271        tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1272        tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1273        tbl.cell(3, 1)
1274            .unwrap()
1275            .set_text("456 Enterprise Blvd, Austin, TX 78701");
1276    }
1277
1278    doc.add_paragraph("");
1279
1280    // ── Line Items ──
1281    {
1282        let mut tbl = doc
1283            .add_table(8, 5)
1284            .borders(BorderStyle::Single, 2, "B22222")
1285            .width_pct(100.0);
1286        let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1287        for (c, h) in hdrs.iter().enumerate() {
1288            tbl.cell(0, c).unwrap().set_text(h);
1289            tbl.cell(0, c).unwrap().shading("B22222");
1290        }
1291        let items = [
1292            (
1293                "ML Infrastructure Setup — Phase 1",
1294                "1",
1295                "$45,000.00",
1296                "$3,881.25",
1297                "$48,881.25",
1298            ),
1299            (
1300                "Data Pipeline Development (160 hrs)",
1301                "160",
1302                "$225.00",
1303                "$3,105.00",
1304                "$39,105.00",
1305            ),
1306            (
1307                "GPU Cluster Configuration",
1308                "1",
1309                "$12,000.00",
1310                "$1,035.00",
1311                "$13,035.00",
1312            ),
1313            (
1314                "API Gateway Implementation",
1315                "1",
1316                "$18,500.00",
1317                "$1,595.63",
1318                "$20,095.63",
1319            ),
1320            (
1321                "Load Testing & QA (80 hrs)",
1322                "80",
1323                "$195.00",
1324                "$1,345.50",
1325                "$16,945.50",
1326            ),
1327            (
1328                "Documentation & Training",
1329                "1",
1330                "$8,000.00",
1331                "$690.00",
1332                "$8,690.00",
1333            ),
1334            (
1335                "Project Management (3 months)",
1336                "3",
1337                "$6,500.00",
1338                "$1,679.63",
1339                "$21,179.63",
1340            ),
1341        ];
1342        for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1343            let r = i + 1;
1344            tbl.cell(r, 0).unwrap().set_text(desc);
1345            tbl.cell(r, 1).unwrap().set_text(qty);
1346            tbl.cell(r, 2).unwrap().set_text(unit);
1347            tbl.cell(r, 3).unwrap().set_text(tax);
1348            tbl.cell(r, 4).unwrap().set_text(amt);
1349            if i % 2 == 0 {
1350                for c in 0..5 {
1351                    tbl.cell(r, c).unwrap().shading("FAF0F0");
1352                }
1353            }
1354        }
1355    }
1356
1357    doc.add_paragraph("");
1358
1359    // ── Totals ──
1360    {
1361        let mut tbl = doc
1362            .add_table(4, 2)
1363            .borders(BorderStyle::Single, 2, "B22222")
1364            .width(Length::inches(3.0))
1365            .alignment(Alignment::Right);
1366        tbl.cell(0, 0).unwrap().set_text("Subtotal");
1367        tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1368        tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1369        tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1370        tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1371        tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1372        tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1373        tbl.cell(3, 0).unwrap().shading("B22222");
1374        tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1375        tbl.cell(3, 1).unwrap().shading("B22222");
1376    }
1377
1378    doc.add_paragraph("");
1379    doc.add_paragraph("");
1380
1381    // ── Payment Details ──
1382    {
1383        let mut p = doc.add_paragraph("");
1384        p.add_run("Payment Details")
1385            .bold(true)
1386            .size(14.0)
1387            .color("333333");
1388    }
1389    doc.add_paragraph("Bank: Silicon Valley Bank")
1390        .indent_left(Length::inches(0.3));
1391    doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1392        .indent_left(Length::inches(0.3));
1393    doc.add_paragraph("Routing: 121140399")
1394        .indent_left(Length::inches(0.3));
1395    doc.add_paragraph("Swift: SVBKUS6S")
1396        .indent_left(Length::inches(0.3));
1397
1398    doc.add_paragraph("");
1399
1400    doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1401        .shading("FAF0F0")
1402        .border_all(BorderStyle::Single, 2, "B22222");
1403
1404    doc
1405}
1406
1407// =============================================================================
1408// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1409// =============================================================================
1410fn generate_report(_samples_dir: &Path) -> Document {
1411    let mut doc = Document::new();
1412
1413    // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1414    doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1415    doc.set_margins(
1416        Length::inches(1.0),
1417        Length::inches(1.0),
1418        Length::inches(1.0),
1419        Length::inches(1.0),
1420    );
1421    doc.set_title("Q4 2025 Environmental Impact Report");
1422    doc.set_author("Walter White");
1423    doc.set_subject("Quarterly Environmental Report");
1424    doc.set_keywords("environment, sustainability, report, Tensorbee");
1425
1426    doc.set_different_first_page(true);
1427    doc.set_first_page_header("");
1428    doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1429    doc.set_footer("CONFIDENTIAL — Page");
1430
1431    // ── Cover ──
1432    let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1433    doc.add_background_image(&cover_bg, "report_cover.png");
1434
1435    for _ in 0..5 {
1436        doc.add_paragraph("");
1437    }
1438    {
1439        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440        p.add_run("Q4 2025")
1441            .bold(true)
1442            .size(48.0)
1443            .color("FFFFFF")
1444            .font("Georgia");
1445    }
1446    {
1447        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1448        p.add_run("Environmental Impact Report")
1449            .size(24.0)
1450            .color("90EE90")
1451            .font("Georgia")
1452            .italic(true);
1453    }
1454    doc.add_paragraph("");
1455    {
1456        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1457        p.add_run("Tensorbee — Sustainability Division")
1458            .size(14.0)
1459            .color("C0C0C0");
1460    }
1461    {
1462        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1463        p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1464            .size(11.0)
1465            .color("AAAAAA");
1466    }
1467
1468    // ── TOC ──
1469    doc.add_paragraph("").page_break_before(true);
1470    doc.insert_toc(doc.content_count(), 3);
1471
1472    // ── Section 1: Executive Overview ──
1473    doc.add_paragraph("").page_break_before(true);
1474    doc.add_paragraph("Executive Overview").style("Heading1");
1475    doc.add_paragraph(
1476        "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1477         sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1478         to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1479         100% renewable energy in our primary data centers and the launch of our carbon offset \
1480         marketplace.",
1481    )
1482    .first_line_indent(Length::inches(0.3));
1483
1484    // Image: performance chart
1485    let chart_img = create_chart_png(400, 200);
1486    doc.add_paragraph("");
1487    doc.add_picture(
1488        &chart_img,
1489        "performance_chart.png",
1490        Length::inches(5.0),
1491        Length::inches(2.5),
1492    )
1493    .alignment(Alignment::Center);
1494    {
1495        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1496        p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1497            .italic(true)
1498            .size(9.0)
1499            .color("666666");
1500    }
1501
1502    // ── Section 2: Energy Consumption ──
1503    doc.add_paragraph("").page_break_before(true);
1504    doc.add_paragraph("Energy Consumption").style("Heading1");
1505
1506    doc.add_paragraph("Data Center Operations")
1507        .style("Heading2");
1508    doc.add_paragraph(
1509        "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1510         a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1511         server consolidation.",
1512    );
1513
1514    // Data table
1515    {
1516        let mut tbl = doc
1517            .add_table(5, 4)
1518            .borders(BorderStyle::Single, 4, "2D5016")
1519            .width_pct(100.0);
1520        let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1521        for (c, h) in hdrs.iter().enumerate() {
1522            tbl.cell(0, c).unwrap().set_text(h);
1523            tbl.cell(0, c).unwrap().shading("2D5016");
1524        }
1525        let rows = [
1526            ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1527            ("Dublin (EU)", "2.1", "1.2", "1.18"),
1528            ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1529            ("TOTAL", "7.1", "4.2", "1.17 avg"),
1530        ];
1531        for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1532            tbl.cell(i + 1, 0).unwrap().set_text(dc);
1533            tbl.cell(i + 1, 1).unwrap().set_text(cap);
1534            tbl.cell(i + 1, 2).unwrap().set_text(use_);
1535            tbl.cell(i + 1, 3).unwrap().set_text(pue);
1536            if i == 3 {
1537                for c in 0..4 {
1538                    tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1539                }
1540            }
1541        }
1542    }
1543
1544    doc.add_paragraph("");
1545
1546    doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1547    doc.add_paragraph("Breakdown of energy sources across all facilities:");
1548
1549    doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1550    doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1551    doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1552    doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1553
1554    // ── Section 3: Water & Waste ──
1555    doc.add_paragraph("Water & Waste Management")
1556        .style("Heading1");
1557
1558    doc.add_paragraph("Water Usage").style("Heading2");
1559    doc.add_paragraph(
1560        "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1561         Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1562    );
1563
1564    doc.add_paragraph("Waste Reduction").style("Heading2");
1565    doc.add_paragraph("E-waste management results for Q4:")
1566        .keep_with_next(true);
1567    {
1568        let mut tbl = doc
1569            .add_table(4, 3)
1570            .borders(BorderStyle::Single, 2, "6B8E23")
1571            .width_pct(80.0);
1572        tbl.cell(0, 0).unwrap().set_text("Category");
1573        tbl.cell(0, 0).unwrap().shading("6B8E23");
1574        tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1575        tbl.cell(0, 1).unwrap().shading("6B8E23");
1576        tbl.cell(0, 2).unwrap().set_text("Recycled %");
1577        tbl.cell(0, 2).unwrap().shading("6B8E23");
1578        let rows = [
1579            ("Server Hardware", "2,450", "94%"),
1580            ("Networking Equipment", "820", "91%"),
1581            ("Storage Media", "340", "99%"),
1582        ];
1583        for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1584            tbl.cell(i + 1, 0).unwrap().set_text(cat);
1585            tbl.cell(i + 1, 1).unwrap().set_text(wt);
1586            tbl.cell(i + 1, 2).unwrap().set_text(pct);
1587        }
1588    }
1589
1590    // ── Section 4: Initiatives ──
1591    doc.add_paragraph("").page_break_before(true);
1592    doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1593
1594    doc.add_paragraph("Planned Programs").style("Heading2");
1595    doc.add_numbered_list_item(
1596        "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1597        0,
1598    );
1599    doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1600    doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1601    doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1602
1603    doc.add_paragraph("Investment Targets").style("Heading2");
1604    doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1605        .keep_with_next(true);
1606    {
1607        let mut tbl = doc
1608            .add_table(5, 2)
1609            .borders(BorderStyle::Single, 4, "2D5016");
1610        tbl.cell(0, 0).unwrap().set_text("Initiative");
1611        tbl.cell(0, 0).unwrap().shading("2D5016");
1612        tbl.cell(0, 1).unwrap().set_text("Budget");
1613        tbl.cell(0, 1).unwrap().shading("2D5016");
1614        let rows = [
1615            ("Battery Storage", "$1.2M"),
1616            ("Immersion Cooling Pilot", "$800K"),
1617            ("Solar Panel Expansion", "$2.1M"),
1618            ("Carbon Credits", "$500K"),
1619        ];
1620        for (i, (init, budget)) in rows.iter().enumerate() {
1621            tbl.cell(i + 1, 0).unwrap().set_text(init);
1622            tbl.cell(i + 1, 1).unwrap().set_text(budget);
1623            if i % 2 == 0 {
1624                tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1625                tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1626            }
1627        }
1628    }
1629
1630    // Image: sustainability roadmap
1631    let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1632    doc.add_paragraph("");
1633    doc.add_picture(
1634        &roadmap_img,
1635        "roadmap.png",
1636        Length::inches(6.0),
1637        Length::inches(1.2),
1638    )
1639    .alignment(Alignment::Center);
1640    {
1641        let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1642        p.add_run("Figure 2: 2026 Sustainability Roadmap")
1643            .italic(true)
1644            .size(9.0)
1645            .color("666666");
1646    }
1647
1648    doc.add_paragraph("");
1649    doc.add_paragraph(
1650        "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1651    )
1652    .shading("FFFAF0")
1653    .border_all(BorderStyle::Single, 2, "2D5016");
1654
1655    doc
1656}
Source

pub fn set_border_all( &mut self, style: BorderStyle, size_eighths_pt: u32, color: &str, )

Add a border to all sides in place.

Source

pub fn border_bottom( self, style: BorderStyle, size_eighths_pt: u32, color: &str, ) -> Self

Add a bottom border only.

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

pub fn set_border_bottom( &mut self, style: BorderStyle, size_eighths_pt: u32, color: &str, )

Add a bottom border in place.

Source

pub fn add_tab_stop(self, alignment: TabAlignment, position: Length) -> Self

Add a tab stop.

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

pub fn set_add_tab_stop(&mut self, alignment: TabAlignment, position: Length)

Add a tab stop in place.

Source

pub fn add_tab_stop_with_leader( self, alignment: TabAlignment, position: Length, leader: TabLeader, ) -> Self

Add a tab stop with a leader character.

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

pub fn set_add_tab_stop_with_leader( &mut self, alignment: TabAlignment, position: Length, leader: TabLeader, )

Add a tab stop with a leader character in place.

Source

pub fn outline_level(self, level: u32) -> Self

Set outline level (0–8, used for TOC generation).

Source

pub fn set_outline_level(&mut self, level: u32)

Set outline level in place.

Source

pub fn section_break(self, break_type: SectionBreak) -> Self

Add a section break after this paragraph.

This creates a <w:sectPr> inside the paragraph’s properties, ending the current section at this paragraph.

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

pub fn set_section_break(&mut self, break_type: SectionBreak)

Add a section break after this paragraph in place.

Source

pub fn section_landscape(self) -> Self

Set the section ending at this paragraph to landscape orientation.

Sets page dimensions to 11“ x 8.5“ (US Letter landscape). Must be combined with section_break() to create a section break.

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

pub fn set_section_landscape(&mut self)

Set the section ending at this paragraph to landscape in place.

Source

pub fn section_portrait(self) -> Self

Set the section ending at this paragraph to portrait orientation.

Sets page dimensions to 8.5“ x 11“ (US Letter portrait). Must be combined with section_break() to create a section break.

Source

pub fn set_section_portrait(&mut self)

Set the section ending at this paragraph to portrait in place.

Source

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

Set custom page dimensions for the section ending at this paragraph.

Source

pub fn set_section_page_size(&mut self, width: Length, height: Length)

Set custom page dimensions for the section in place.

Auto Trait Implementations§

§

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

§

impl<'a> Freeze for Paragraph<'a>

§

impl<'a> RefUnwindSafe for Paragraph<'a>

§

impl<'a> Send for Paragraph<'a>

§

impl<'a> Sync for Paragraph<'a>

§

impl<'a> Unpin for Paragraph<'a>

§

impl<'a> UnsafeUnpin for Paragraph<'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<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.