pub struct Paragraph<'a> { /* private fields */ }Expand description
A mutable reference to a paragraph in a document.
Implementations§
Source§impl<'a> Paragraph<'a>
impl<'a> Paragraph<'a>
Sourcepub fn add_footnote_ref(&mut self, id: i32)
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.
Sourcepub fn add_run(&mut self, text: &str) -> Run<'_>
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?
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
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}Sourcepub fn add_line_break(&mut self)
pub fn add_line_break(&mut self)
Add a line break in its own run.
Sourcepub fn add_hyperlink(&mut self, text: &str, relationship_id: &str) -> Run<'_>
pub fn add_hyperlink(&mut self, text: &str, relationship_id: &str) -> 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.
Sourcepub fn runs(&self) -> impl Iterator<Item = RunRef<'_>>
pub fn runs(&self) -> impl Iterator<Item = RunRef<'_>>
Get an iterator over immutable run references.
Sourcepub fn alignment(self, align: Alignment) -> Self
pub fn alignment(self, align: Alignment) -> Self
Set the paragraph alignment.
Examples found in repository?
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
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}Sourcepub fn set_alignment(&mut self, align: Alignment)
pub fn set_alignment(&mut self, align: Alignment)
Set the paragraph alignment in place.
Sourcepub fn set_alignment_value(&mut self, align: Option<Alignment>)
pub fn set_alignment_value(&mut self, align: Option<Alignment>)
Set or clear direct paragraph alignment.
Sourcepub fn style(self, style_id: &str) -> Self
pub fn style(self, style_id: &str) -> Self
Set the paragraph style by ID.
Examples found in repository?
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
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}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}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}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}Sourcepub fn numbering(self, num_id: u32, level: u32) -> Self
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.
Sourcepub fn set_numbering(&mut self, num_id: u32, level: u32) -> bool
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.
Sourcepub fn set_numbering_value(&mut self, numbering: Option<(u32, u32)>) -> bool
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.
Sourcepub fn space_before(self, length: Length) -> Self
pub fn space_before(self, length: Length) -> Self
Set space before the paragraph.
Examples found in repository?
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}Sourcepub fn set_space_before(&mut self, length: Length)
pub fn set_space_before(&mut self, length: Length)
Set space before the paragraph in place.
Sourcepub fn set_space_before_value(&mut self, length: Option<Length>)
pub fn set_space_before_value(&mut self, length: Option<Length>)
Set or clear direct space before the paragraph.
Sourcepub fn space_after(self, length: Length) -> Self
pub fn space_after(self, length: Length) -> Self
Set space after the paragraph.
Examples found in repository?
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}Sourcepub fn set_space_after(&mut self, length: Length)
pub fn set_space_after(&mut self, length: Length)
Set space after the paragraph in place.
Sourcepub fn set_space_after_value(&mut self, length: Option<Length>)
pub fn set_space_after_value(&mut self, length: Option<Length>)
Set or clear direct space after the paragraph.
Sourcepub fn indent_left(self, length: Length) -> Self
pub fn indent_left(self, length: Length) -> Self
Set left indentation.
Examples found in repository?
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}Sourcepub fn set_indent_left(&mut self, length: Length)
pub fn set_indent_left(&mut self, length: Length)
Set left indentation in place.
Sourcepub fn set_indent_left_value(&mut self, length: Option<Length>)
pub fn set_indent_left_value(&mut self, length: Option<Length>)
Set or clear direct left indentation.
Sourcepub fn indent_right(self, length: Length) -> Self
pub fn indent_right(self, length: Length) -> Self
Set right indentation.
Examples found in repository?
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}Sourcepub fn set_indent_right(&mut self, length: Length)
pub fn set_indent_right(&mut self, length: Length)
Set right indentation in place.
Sourcepub fn set_indent_right_value(&mut self, length: Option<Length>)
pub fn set_indent_right_value(&mut self, length: Option<Length>)
Set or clear direct right indentation.
Sourcepub fn first_line_indent(self, length: Length) -> Self
pub fn first_line_indent(self, length: Length) -> Self
Set first line indent.
Examples found in repository?
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}Sourcepub fn set_first_line_indent(&mut self, length: Length)
pub fn set_first_line_indent(&mut self, length: Length)
Set first line indent in place.
Sourcepub fn set_signed_first_line_indent_value(&mut self, length: Option<Length>)
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.
Sourcepub fn hanging_indent(self, length: Length) -> Self
pub fn hanging_indent(self, length: Length) -> Self
Set hanging indent.
Examples found in repository?
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}Sourcepub fn set_hanging_indent(&mut self, length: Length)
pub fn set_hanging_indent(&mut self, length: Length)
Set hanging indent in place.
Sourcepub fn keep_with_next(self, val: bool) -> Self
pub fn keep_with_next(self, val: bool) -> Self
Set keep with next paragraph.
Examples found in repository?
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}Sourcepub fn set_keep_with_next(&mut self, val: bool)
pub fn set_keep_with_next(&mut self, val: bool)
Set keep with next paragraph in place.
Sourcepub fn set_keep_with_next_value(&mut self, val: Option<bool>)
pub fn set_keep_with_next_value(&mut self, val: Option<bool>)
Set or clear direct keep-with-next formatting.
Sourcepub fn keep_together(self, val: bool) -> Self
pub fn keep_together(self, val: bool) -> Self
Set keep lines together.
Examples found in repository?
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}Sourcepub fn set_keep_together(&mut self, val: bool)
pub fn set_keep_together(&mut self, val: bool)
Set keep lines together in place.
Sourcepub fn set_keep_together_value(&mut self, val: Option<bool>)
pub fn set_keep_together_value(&mut self, val: Option<bool>)
Set or clear direct keep-together formatting.
Sourcepub fn page_break_before(self, val: bool) -> Self
pub fn page_break_before(self, val: bool) -> Self
Set page break before.
Examples found in repository?
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
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}Sourcepub fn set_page_break_before(&mut self, val: bool)
pub fn set_page_break_before(&mut self, val: bool)
Set page break before in place.
Sourcepub fn set_page_break_before_value(&mut self, val: Option<bool>)
pub fn set_page_break_before_value(&mut self, val: Option<bool>)
Set or clear direct page-break-before formatting.
Sourcepub fn widow_control(self, val: bool) -> Self
pub fn widow_control(self, val: bool) -> Self
Set widow/orphan control.
Examples found in repository?
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}Sourcepub fn set_widow_control(&mut self, val: bool)
pub fn set_widow_control(&mut self, val: bool)
Set widow/orphan control in place.
Sourcepub fn set_widow_control_value(&mut self, val: Option<bool>)
pub fn set_widow_control_value(&mut self, val: Option<bool>)
Set or clear direct widow-control formatting.
Sourcepub fn line_spacing(self, pt: f64) -> Self
pub fn line_spacing(self, pt: f64) -> Self
Set line spacing in points with “exact” rule.
Examples found in repository?
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}Sourcepub fn set_line_spacing(&mut self, pt: f64)
pub fn set_line_spacing(&mut self, pt: f64)
Set exact line spacing in place.
Sourcepub fn clear_line_spacing(&mut self)
pub fn clear_line_spacing(&mut self)
Clear direct line spacing.
Sourcepub fn line_spacing_multiple(self, multiple: f64) -> Self
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?
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}Sourcepub fn set_line_spacing_multiple(&mut self, multiple: f64)
pub fn set_line_spacing_multiple(&mut self, multiple: f64)
Set multiplied line spacing in place.
Sourcepub fn shading(self, fill_color: &str) -> Self
pub fn shading(self, fill_color: &str) -> Self
Set a background/shading fill color (hex string, e.g. “FFFF00”).
Examples found in repository?
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}Sourcepub fn set_shading(&mut self, fill_color: &str)
pub fn set_shading(&mut self, fill_color: &str)
Set a background/shading fill color in place.
Sourcepub fn border_all(
self,
style: BorderStyle,
size_eighths_pt: u32,
color: &str,
) -> Self
pub fn border_all( self, style: BorderStyle, size_eighths_pt: u32, color: &str, ) -> Self
Add a border to all sides.
Examples found in repository?
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}Sourcepub fn set_border_all(
&mut self,
style: BorderStyle,
size_eighths_pt: u32,
color: &str,
)
pub fn set_border_all( &mut self, style: BorderStyle, size_eighths_pt: u32, color: &str, )
Add a border to all sides in place.
Sourcepub fn border_bottom(
self,
style: BorderStyle,
size_eighths_pt: u32,
color: &str,
) -> Self
pub fn border_bottom( self, style: BorderStyle, size_eighths_pt: u32, color: &str, ) -> Self
Add a bottom border only.
Examples found in repository?
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}Sourcepub fn set_border_bottom(
&mut self,
style: BorderStyle,
size_eighths_pt: u32,
color: &str,
)
pub fn set_border_bottom( &mut self, style: BorderStyle, size_eighths_pt: u32, color: &str, )
Add a bottom border in place.
Sourcepub fn add_tab_stop(self, alignment: TabAlignment, position: Length) -> Self
pub fn add_tab_stop(self, alignment: TabAlignment, position: Length) -> Self
Add a tab stop.
Examples found in repository?
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}Sourcepub fn set_add_tab_stop(&mut self, alignment: TabAlignment, position: Length)
pub fn set_add_tab_stop(&mut self, alignment: TabAlignment, position: Length)
Add a tab stop in place.
Sourcepub fn add_tab_stop_with_leader(
self,
alignment: TabAlignment,
position: Length,
leader: TabLeader,
) -> Self
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?
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}Sourcepub fn set_add_tab_stop_with_leader(
&mut self,
alignment: TabAlignment,
position: Length,
leader: TabLeader,
)
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.
Sourcepub fn outline_level(self, level: u32) -> Self
pub fn outline_level(self, level: u32) -> Self
Set outline level (0–8, used for TOC generation).
Sourcepub fn set_outline_level(&mut self, level: u32)
pub fn set_outline_level(&mut self, level: u32)
Set outline level in place.
Sourcepub fn section_break(self, break_type: SectionBreak) -> Self
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?
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}Sourcepub fn set_section_break(&mut self, break_type: SectionBreak)
pub fn set_section_break(&mut self, break_type: SectionBreak)
Add a section break after this paragraph in place.
Sourcepub fn section_landscape(self) -> Self
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?
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}Sourcepub fn set_section_landscape(&mut self)
pub fn set_section_landscape(&mut self)
Set the section ending at this paragraph to landscape in place.
Sourcepub fn section_portrait(self) -> Self
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.
Sourcepub fn set_section_portrait(&mut self)
pub fn set_section_portrait(&mut self)
Set the section ending at this paragraph to portrait in place.
Sourcepub fn section_page_size(self, width: Length, height: Length) -> Self
pub fn section_page_size(self, width: Length, height: Length) -> Self
Set custom page dimensions for the section ending at this paragraph.
Sourcepub fn set_section_page_size(&mut self, width: Length, height: Length)
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<U, T> ToOwnedObj<U> for Twhere
U: FromObjRef<T>,
impl<U, T> ToOwnedObj<U> for Twhere
U: FromObjRef<T>,
Source§fn to_owned_obj(&self, data: FontData<'_>) -> U
fn to_owned_obj(&self, data: FontData<'_>) -> U
T, using the provided data to resolve any offsets.