pub struct StyleBuilder { /* private fields */ }Expand description
Builder for creating a new paragraph style.
Implementations§
Source§impl StyleBuilder
impl StyleBuilder
Sourcepub fn paragraph(style_id: &str, name: &str) -> Self
pub fn paragraph(style_id: &str, name: &str) -> Self
Create a new paragraph style builder.
Examples found in repository?
examples/generate_all_samples.rs (line 588)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}
684
685// =============================================================================
686// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
687// =============================================================================
688fn generate_proposal(_samples_dir: &Path) -> Document {
689 let mut doc = Document::new();
690
691 // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
692 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
693 doc.set_margins(
694 Length::inches(1.0),
695 Length::inches(1.0),
696 Length::inches(1.0),
697 Length::inches(1.0),
698 );
699 doc.set_title("AI Platform Modernization Proposal");
700 doc.set_author("Walter White");
701 doc.set_subject("Technology Proposal");
702 doc.set_keywords("proposal, AI, modernization, Tensorbee");
703
704 // Banner header
705 let logo_img = create_logo_png(220, 48);
706 let banner = build_header_banner_xml(
707 "rId1",
708 &BannerOpts {
709 bg_color: "1B2A4A",
710 banner_width: 7772400,
711 banner_height: 914400,
712 logo_width: 2011680,
713 logo_height: 438912,
714 logo_x_offset: 295125,
715 logo_y_offset: 237744,
716 },
717 );
718 doc.set_raw_header_with_images(
719 banner,
720 &[("rId1", &logo_img, "logo.png")],
721 rdocx_oxml::header_footer::HdrFtrType::Default,
722 );
723 doc.set_different_first_page(true);
724 let cover_banner = build_header_banner_xml(
725 "rId1",
726 &BannerOpts {
727 bg_color: "C5922E",
728 banner_width: 7772400,
729 banner_height: 914400,
730 logo_width: 2011680,
731 logo_height: 438912,
732 logo_x_offset: 295125,
733 logo_y_offset: 237744,
734 },
735 );
736 doc.set_raw_header_with_images(
737 cover_banner,
738 &[("rId1", &logo_img, "logo.png")],
739 rdocx_oxml::header_footer::HdrFtrType::First,
740 );
741 doc.set_margins(
742 Length::twips(2292),
743 Length::twips(1440),
744 Length::twips(1440),
745 Length::twips(1440),
746 );
747 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
748 doc.set_footer("Tensorbee — Confidential");
749
750 // Custom styles
751 doc.add_style(
752 StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
753 .based_on("Normal")
754 .run_properties({
755 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
756 rpr.bold = Some(true);
757 rpr.font_ascii = Some("Georgia".to_string());
758 rpr.font_hansi = Some("Georgia".to_string());
759 rpr.sz = Some(rdocx_oxml::HalfPoint(56)); // half-points → 28pt
760 rpr.color = Some("1B2A4A".to_string());
761 rpr
762 }),
763 );
764
765 // ── Cover Page ──
766 for _ in 0..4 {
767 doc.add_paragraph("");
768 }
769 doc.add_paragraph("AI Platform Modernization")
770 .style("ProposalTitle")
771 .alignment(Alignment::Center);
772 doc.add_paragraph("")
773 .alignment(Alignment::Center)
774 .border_bottom(BorderStyle::Single, 8, "C5922E");
775 {
776 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
777 p.add_run("Prepared for Global Financial Corp.")
778 .size(14.0)
779 .color("666666")
780 .italic(true);
781 }
782 {
783 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
784 p.add_run("Prepared by: Walter White, CEO — Tensorbee")
785 .size(12.0)
786 .color("888888");
787 }
788 {
789 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
790 p.add_run("February 22, 2026").size(12.0).color("888888");
791 }
792
793 // ── TOC ──
794 doc.add_paragraph("").page_break_before(true);
795 doc.add_paragraph("Table of Contents").style("Heading1");
796 // We'll add a manual-style TOC since the insert_toc goes at a specific position
797 doc.insert_toc(doc.content_count(), 2);
798
799 // ── Executive Summary ──
800 doc.add_paragraph("").page_break_before(true);
801 doc.add_paragraph("Executive Summary").style("Heading1");
802 doc.add_paragraph(
803 "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
804 transitioning from legacy batch-processing systems to a real-time inference architecture. \
805 This transformation will reduce model deployment time from weeks to hours, improve \
806 prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
807 )
808 .first_line_indent(Length::inches(0.3));
809 doc.add_paragraph(
810 "The project spans three phases over 18 months, with a total investment of $2.8M. \
811 Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
812 similar transformations for three Fortune 500 financial institutions.",
813 )
814 .first_line_indent(Length::inches(0.3));
815
816 // Key metrics table
817 doc.add_paragraph("");
818 {
819 let mut tbl = doc
820 .add_table(5, 2)
821 .borders(BorderStyle::Single, 4, "1B2A4A")
822 .width_pct(80.0);
823 tbl.cell(0, 0).unwrap().set_text("Key Metric");
824 tbl.cell(0, 0).unwrap().shading("1B2A4A");
825 tbl.cell(0, 1).unwrap().set_text("Value");
826 tbl.cell(0, 1).unwrap().shading("1B2A4A");
827 let rows = [
828 ("Total Investment", "$2.8M"),
829 ("Annual Savings", "$4.2M"),
830 ("ROI (Year 1)", "150%"),
831 ("Timeline", "18 months"),
832 ];
833 for (i, (k, v)) in rows.iter().enumerate() {
834 tbl.cell(i + 1, 0).unwrap().set_text(k);
835 if i % 2 == 0 {
836 tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
837 }
838 tbl.cell(i + 1, 1).unwrap().set_text(v);
839 if i % 2 == 0 {
840 tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
841 }
842 }
843 }
844
845 // ── Problem Statement ──
846 doc.add_paragraph("").page_break_before(true);
847 doc.add_paragraph("Problem Statement").style("Heading1");
848 doc.add_paragraph(
849 "Global Financial Corp's current AI infrastructure faces three critical challenges:",
850 );
851 doc.add_numbered_list_item("Deployment Latency — New models take 3-4 weeks to deploy to production, compared to the industry benchmark of 2-3 days.", 0);
852 doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
853 doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
854
855 // ── Proposed Solution ──
856 doc.add_paragraph("").page_break_before(true);
857 doc.add_paragraph("Proposed Solution").style("Heading1");
858
859 doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
860 .style("Heading2");
861 doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
862 doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
863 doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
864
865 doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
866 .style("Heading2");
867 doc.add_bullet_list_item(
868 "Real-time feature engineering pipeline (Apache Kafka + Flink)",
869 0,
870 );
871 doc.add_bullet_list_item("A/B testing framework for model variants", 0);
872 doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
873
874 doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
875 .style("Heading2");
876 doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
877 doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
878 doc.add_bullet_list_item(
879 "Self-service model deployment portal for data science teams",
880 0,
881 );
882
883 // ── Budget ──
884 doc.add_paragraph("Budget Breakdown").style("Heading1");
885 {
886 let mut tbl = doc
887 .add_table(6, 3)
888 .borders(BorderStyle::Single, 4, "1B2A4A")
889 .width_pct(100.0);
890 tbl.cell(0, 0).unwrap().set_text("Category");
891 tbl.cell(0, 0).unwrap().shading("1B2A4A");
892 tbl.cell(0, 1).unwrap().set_text("Cost");
893 tbl.cell(0, 1).unwrap().shading("1B2A4A");
894 tbl.cell(0, 2).unwrap().set_text("Notes");
895 tbl.cell(0, 2).unwrap().shading("1B2A4A");
896 let items = [
897 (
898 "Infrastructure",
899 "$450,000",
900 "Cloud compute + GPU instances",
901 ),
902 ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
903 (
904 "Software Licenses",
905 "$350,000",
906 "Kafka, monitoring, CI/CD tools",
907 ),
908 (
909 "Training & Enablement",
910 "$200,000",
911 "Team training, documentation",
912 ),
913 ("Contingency (10%)", "$200,000", "Risk buffer"),
914 ];
915 for (i, (cat, cost, note)) in items.iter().enumerate() {
916 tbl.cell(i + 1, 0).unwrap().set_text(cat);
917 tbl.cell(i + 1, 1).unwrap().set_text(cost);
918 tbl.cell(i + 1, 2).unwrap().set_text(note);
919 if i % 2 == 0 {
920 tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
921 tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
922 tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
923 }
924 }
925 }
926
927 // ── Closing ──
928 doc.add_paragraph("");
929 doc.add_paragraph("Next Steps").style("Heading1");
930 doc.add_numbered_list_item(
931 "Schedule technical deep-dive with GFC engineering team (Week 1)",
932 0,
933 );
934 doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
935 doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
936
937 doc.add_paragraph("");
938 {
939 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
940 p.add_run("Walter White")
941 .bold(true)
942 .size(14.0)
943 .color("1B2A4A");
944 }
945 {
946 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
947 p.add_run("CEO, Tensorbee | walter@tensorbee.com")
948 .size(10.0)
949 .color("888888");
950 }
951
952 doc
953}Sourcepub fn based_on(self, style_id: &str) -> Self
pub fn based_on(self, style_id: &str) -> Self
Set the parent style this one inherits from.
Examples found in repository?
examples/generate_all_samples.rs (line 589)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}
684
685// =============================================================================
686// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
687// =============================================================================
688fn generate_proposal(_samples_dir: &Path) -> Document {
689 let mut doc = Document::new();
690
691 // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
692 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
693 doc.set_margins(
694 Length::inches(1.0),
695 Length::inches(1.0),
696 Length::inches(1.0),
697 Length::inches(1.0),
698 );
699 doc.set_title("AI Platform Modernization Proposal");
700 doc.set_author("Walter White");
701 doc.set_subject("Technology Proposal");
702 doc.set_keywords("proposal, AI, modernization, Tensorbee");
703
704 // Banner header
705 let logo_img = create_logo_png(220, 48);
706 let banner = build_header_banner_xml(
707 "rId1",
708 &BannerOpts {
709 bg_color: "1B2A4A",
710 banner_width: 7772400,
711 banner_height: 914400,
712 logo_width: 2011680,
713 logo_height: 438912,
714 logo_x_offset: 295125,
715 logo_y_offset: 237744,
716 },
717 );
718 doc.set_raw_header_with_images(
719 banner,
720 &[("rId1", &logo_img, "logo.png")],
721 rdocx_oxml::header_footer::HdrFtrType::Default,
722 );
723 doc.set_different_first_page(true);
724 let cover_banner = build_header_banner_xml(
725 "rId1",
726 &BannerOpts {
727 bg_color: "C5922E",
728 banner_width: 7772400,
729 banner_height: 914400,
730 logo_width: 2011680,
731 logo_height: 438912,
732 logo_x_offset: 295125,
733 logo_y_offset: 237744,
734 },
735 );
736 doc.set_raw_header_with_images(
737 cover_banner,
738 &[("rId1", &logo_img, "logo.png")],
739 rdocx_oxml::header_footer::HdrFtrType::First,
740 );
741 doc.set_margins(
742 Length::twips(2292),
743 Length::twips(1440),
744 Length::twips(1440),
745 Length::twips(1440),
746 );
747 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
748 doc.set_footer("Tensorbee — Confidential");
749
750 // Custom styles
751 doc.add_style(
752 StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
753 .based_on("Normal")
754 .run_properties({
755 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
756 rpr.bold = Some(true);
757 rpr.font_ascii = Some("Georgia".to_string());
758 rpr.font_hansi = Some("Georgia".to_string());
759 rpr.sz = Some(rdocx_oxml::HalfPoint(56)); // half-points → 28pt
760 rpr.color = Some("1B2A4A".to_string());
761 rpr
762 }),
763 );
764
765 // ── Cover Page ──
766 for _ in 0..4 {
767 doc.add_paragraph("");
768 }
769 doc.add_paragraph("AI Platform Modernization")
770 .style("ProposalTitle")
771 .alignment(Alignment::Center);
772 doc.add_paragraph("")
773 .alignment(Alignment::Center)
774 .border_bottom(BorderStyle::Single, 8, "C5922E");
775 {
776 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
777 p.add_run("Prepared for Global Financial Corp.")
778 .size(14.0)
779 .color("666666")
780 .italic(true);
781 }
782 {
783 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
784 p.add_run("Prepared by: Walter White, CEO — Tensorbee")
785 .size(12.0)
786 .color("888888");
787 }
788 {
789 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
790 p.add_run("February 22, 2026").size(12.0).color("888888");
791 }
792
793 // ── TOC ──
794 doc.add_paragraph("").page_break_before(true);
795 doc.add_paragraph("Table of Contents").style("Heading1");
796 // We'll add a manual-style TOC since the insert_toc goes at a specific position
797 doc.insert_toc(doc.content_count(), 2);
798
799 // ── Executive Summary ──
800 doc.add_paragraph("").page_break_before(true);
801 doc.add_paragraph("Executive Summary").style("Heading1");
802 doc.add_paragraph(
803 "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
804 transitioning from legacy batch-processing systems to a real-time inference architecture. \
805 This transformation will reduce model deployment time from weeks to hours, improve \
806 prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
807 )
808 .first_line_indent(Length::inches(0.3));
809 doc.add_paragraph(
810 "The project spans three phases over 18 months, with a total investment of $2.8M. \
811 Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
812 similar transformations for three Fortune 500 financial institutions.",
813 )
814 .first_line_indent(Length::inches(0.3));
815
816 // Key metrics table
817 doc.add_paragraph("");
818 {
819 let mut tbl = doc
820 .add_table(5, 2)
821 .borders(BorderStyle::Single, 4, "1B2A4A")
822 .width_pct(80.0);
823 tbl.cell(0, 0).unwrap().set_text("Key Metric");
824 tbl.cell(0, 0).unwrap().shading("1B2A4A");
825 tbl.cell(0, 1).unwrap().set_text("Value");
826 tbl.cell(0, 1).unwrap().shading("1B2A4A");
827 let rows = [
828 ("Total Investment", "$2.8M"),
829 ("Annual Savings", "$4.2M"),
830 ("ROI (Year 1)", "150%"),
831 ("Timeline", "18 months"),
832 ];
833 for (i, (k, v)) in rows.iter().enumerate() {
834 tbl.cell(i + 1, 0).unwrap().set_text(k);
835 if i % 2 == 0 {
836 tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
837 }
838 tbl.cell(i + 1, 1).unwrap().set_text(v);
839 if i % 2 == 0 {
840 tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
841 }
842 }
843 }
844
845 // ── Problem Statement ──
846 doc.add_paragraph("").page_break_before(true);
847 doc.add_paragraph("Problem Statement").style("Heading1");
848 doc.add_paragraph(
849 "Global Financial Corp's current AI infrastructure faces three critical challenges:",
850 );
851 doc.add_numbered_list_item("Deployment Latency — New models take 3-4 weeks to deploy to production, compared to the industry benchmark of 2-3 days.", 0);
852 doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
853 doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
854
855 // ── Proposed Solution ──
856 doc.add_paragraph("").page_break_before(true);
857 doc.add_paragraph("Proposed Solution").style("Heading1");
858
859 doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
860 .style("Heading2");
861 doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
862 doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
863 doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
864
865 doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
866 .style("Heading2");
867 doc.add_bullet_list_item(
868 "Real-time feature engineering pipeline (Apache Kafka + Flink)",
869 0,
870 );
871 doc.add_bullet_list_item("A/B testing framework for model variants", 0);
872 doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
873
874 doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
875 .style("Heading2");
876 doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
877 doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
878 doc.add_bullet_list_item(
879 "Self-service model deployment portal for data science teams",
880 0,
881 );
882
883 // ── Budget ──
884 doc.add_paragraph("Budget Breakdown").style("Heading1");
885 {
886 let mut tbl = doc
887 .add_table(6, 3)
888 .borders(BorderStyle::Single, 4, "1B2A4A")
889 .width_pct(100.0);
890 tbl.cell(0, 0).unwrap().set_text("Category");
891 tbl.cell(0, 0).unwrap().shading("1B2A4A");
892 tbl.cell(0, 1).unwrap().set_text("Cost");
893 tbl.cell(0, 1).unwrap().shading("1B2A4A");
894 tbl.cell(0, 2).unwrap().set_text("Notes");
895 tbl.cell(0, 2).unwrap().shading("1B2A4A");
896 let items = [
897 (
898 "Infrastructure",
899 "$450,000",
900 "Cloud compute + GPU instances",
901 ),
902 ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
903 (
904 "Software Licenses",
905 "$350,000",
906 "Kafka, monitoring, CI/CD tools",
907 ),
908 (
909 "Training & Enablement",
910 "$200,000",
911 "Team training, documentation",
912 ),
913 ("Contingency (10%)", "$200,000", "Risk buffer"),
914 ];
915 for (i, (cat, cost, note)) in items.iter().enumerate() {
916 tbl.cell(i + 1, 0).unwrap().set_text(cat);
917 tbl.cell(i + 1, 1).unwrap().set_text(cost);
918 tbl.cell(i + 1, 2).unwrap().set_text(note);
919 if i % 2 == 0 {
920 tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
921 tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
922 tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
923 }
924 }
925 }
926
927 // ── Closing ──
928 doc.add_paragraph("");
929 doc.add_paragraph("Next Steps").style("Heading1");
930 doc.add_numbered_list_item(
931 "Schedule technical deep-dive with GFC engineering team (Week 1)",
932 0,
933 );
934 doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
935 doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
936
937 doc.add_paragraph("");
938 {
939 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
940 p.add_run("Walter White")
941 .bold(true)
942 .size(14.0)
943 .color("1B2A4A");
944 }
945 {
946 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
947 p.add_run("CEO, Tensorbee | walter@tensorbee.com")
948 .size(10.0)
949 .color("888888");
950 }
951
952 doc
953}Sourcepub fn next_style(self, style_id: &str) -> Self
pub fn next_style(self, style_id: &str) -> Self
Set the next style (applied to the following paragraph after pressing Enter).
Sourcepub fn paragraph_properties(self, ppr: CT_PPr) -> Self
pub fn paragraph_properties(self, ppr: CT_PPr) -> Self
Set paragraph properties for this style.
Examples found in repository?
examples/generate_all_samples.rs (lines 590-598)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}Sourcepub fn run_properties(self, rpr: CT_RPr) -> Self
pub fn run_properties(self, rpr: CT_RPr) -> Self
Set run properties for this style.
Examples found in repository?
examples/generate_all_samples.rs (lines 599-604)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}
684
685// =============================================================================
686// 2. PROPOSAL DOCUMENT — Deep navy + gold scheme
687// =============================================================================
688fn generate_proposal(_samples_dir: &Path) -> Document {
689 let mut doc = Document::new();
690
691 // Colors: Navy #1B2A4A, Gold #C5922E, Light #F4F1EB
692 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
693 doc.set_margins(
694 Length::inches(1.0),
695 Length::inches(1.0),
696 Length::inches(1.0),
697 Length::inches(1.0),
698 );
699 doc.set_title("AI Platform Modernization Proposal");
700 doc.set_author("Walter White");
701 doc.set_subject("Technology Proposal");
702 doc.set_keywords("proposal, AI, modernization, Tensorbee");
703
704 // Banner header
705 let logo_img = create_logo_png(220, 48);
706 let banner = build_header_banner_xml(
707 "rId1",
708 &BannerOpts {
709 bg_color: "1B2A4A",
710 banner_width: 7772400,
711 banner_height: 914400,
712 logo_width: 2011680,
713 logo_height: 438912,
714 logo_x_offset: 295125,
715 logo_y_offset: 237744,
716 },
717 );
718 doc.set_raw_header_with_images(
719 banner,
720 &[("rId1", &logo_img, "logo.png")],
721 rdocx_oxml::header_footer::HdrFtrType::Default,
722 );
723 doc.set_different_first_page(true);
724 let cover_banner = build_header_banner_xml(
725 "rId1",
726 &BannerOpts {
727 bg_color: "C5922E",
728 banner_width: 7772400,
729 banner_height: 914400,
730 logo_width: 2011680,
731 logo_height: 438912,
732 logo_x_offset: 295125,
733 logo_y_offset: 237744,
734 },
735 );
736 doc.set_raw_header_with_images(
737 cover_banner,
738 &[("rId1", &logo_img, "logo.png")],
739 rdocx_oxml::header_footer::HdrFtrType::First,
740 );
741 doc.set_margins(
742 Length::twips(2292),
743 Length::twips(1440),
744 Length::twips(1440),
745 Length::twips(1440),
746 );
747 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
748 doc.set_footer("Tensorbee — Confidential");
749
750 // Custom styles
751 doc.add_style(
752 StyleBuilder::paragraph("ProposalTitle", "Proposal Title")
753 .based_on("Normal")
754 .run_properties({
755 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
756 rpr.bold = Some(true);
757 rpr.font_ascii = Some("Georgia".to_string());
758 rpr.font_hansi = Some("Georgia".to_string());
759 rpr.sz = Some(rdocx_oxml::HalfPoint(56)); // half-points → 28pt
760 rpr.color = Some("1B2A4A".to_string());
761 rpr
762 }),
763 );
764
765 // ── Cover Page ──
766 for _ in 0..4 {
767 doc.add_paragraph("");
768 }
769 doc.add_paragraph("AI Platform Modernization")
770 .style("ProposalTitle")
771 .alignment(Alignment::Center);
772 doc.add_paragraph("")
773 .alignment(Alignment::Center)
774 .border_bottom(BorderStyle::Single, 8, "C5922E");
775 {
776 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
777 p.add_run("Prepared for Global Financial Corp.")
778 .size(14.0)
779 .color("666666")
780 .italic(true);
781 }
782 {
783 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
784 p.add_run("Prepared by: Walter White, CEO — Tensorbee")
785 .size(12.0)
786 .color("888888");
787 }
788 {
789 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
790 p.add_run("February 22, 2026").size(12.0).color("888888");
791 }
792
793 // ── TOC ──
794 doc.add_paragraph("").page_break_before(true);
795 doc.add_paragraph("Table of Contents").style("Heading1");
796 // We'll add a manual-style TOC since the insert_toc goes at a specific position
797 doc.insert_toc(doc.content_count(), 2);
798
799 // ── Executive Summary ──
800 doc.add_paragraph("").page_break_before(true);
801 doc.add_paragraph("Executive Summary").style("Heading1");
802 doc.add_paragraph(
803 "Tensorbee proposes a comprehensive modernization of Global Financial Corp's AI platform, \
804 transitioning from legacy batch-processing systems to a real-time inference architecture. \
805 This transformation will reduce model deployment time from weeks to hours, improve \
806 prediction accuracy by an estimated 23%, and deliver $4.2M in annual operational savings.",
807 )
808 .first_line_indent(Length::inches(0.3));
809 doc.add_paragraph(
810 "The project spans three phases over 18 months, with a total investment of $2.8M. \
811 Tensorbee brings deep expertise in ML infrastructure, having successfully completed \
812 similar transformations for three Fortune 500 financial institutions.",
813 )
814 .first_line_indent(Length::inches(0.3));
815
816 // Key metrics table
817 doc.add_paragraph("");
818 {
819 let mut tbl = doc
820 .add_table(5, 2)
821 .borders(BorderStyle::Single, 4, "1B2A4A")
822 .width_pct(80.0);
823 tbl.cell(0, 0).unwrap().set_text("Key Metric");
824 tbl.cell(0, 0).unwrap().shading("1B2A4A");
825 tbl.cell(0, 1).unwrap().set_text("Value");
826 tbl.cell(0, 1).unwrap().shading("1B2A4A");
827 let rows = [
828 ("Total Investment", "$2.8M"),
829 ("Annual Savings", "$4.2M"),
830 ("ROI (Year 1)", "150%"),
831 ("Timeline", "18 months"),
832 ];
833 for (i, (k, v)) in rows.iter().enumerate() {
834 tbl.cell(i + 1, 0).unwrap().set_text(k);
835 if i % 2 == 0 {
836 tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
837 }
838 tbl.cell(i + 1, 1).unwrap().set_text(v);
839 if i % 2 == 0 {
840 tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
841 }
842 }
843 }
844
845 // ── Problem Statement ──
846 doc.add_paragraph("").page_break_before(true);
847 doc.add_paragraph("Problem Statement").style("Heading1");
848 doc.add_paragraph(
849 "Global Financial Corp's current AI infrastructure faces three critical challenges:",
850 );
851 doc.add_numbered_list_item("Deployment Latency — New models take 3-4 weeks to deploy to production, compared to the industry benchmark of 2-3 days.", 0);
852 doc.add_numbered_list_item("Scalability Constraints — The batch processing architecture cannot handle real-time inference demands during peak trading hours.", 0);
853 doc.add_numbered_list_item("Technical Debt — Legacy Python 2.7 codebases and manual deployment scripts create reliability risks and slow iteration speed.", 0);
854
855 // ── Proposed Solution ──
856 doc.add_paragraph("").page_break_before(true);
857 doc.add_paragraph("Proposed Solution").style("Heading1");
858
859 doc.add_paragraph("Phase 1: Foundation (Months 1-6)")
860 .style("Heading2");
861 doc.add_bullet_list_item("Deploy Kubernetes-based ML serving infrastructure", 0);
862 doc.add_bullet_list_item("Implement CI/CD pipeline for model deployment", 0);
863 doc.add_bullet_list_item("Migrate top 5 production models to new platform", 0);
864
865 doc.add_paragraph("Phase 2: Expansion (Months 7-12)")
866 .style("Heading2");
867 doc.add_bullet_list_item(
868 "Real-time feature engineering pipeline (Apache Kafka + Flink)",
869 0,
870 );
871 doc.add_bullet_list_item("A/B testing framework for model variants", 0);
872 doc.add_bullet_list_item("Automated model monitoring and drift detection", 0);
873
874 doc.add_paragraph("Phase 3: Optimization (Months 13-18)")
875 .style("Heading2");
876 doc.add_bullet_list_item("GPU inference optimization (TensorRT/ONNX Runtime)", 0);
877 doc.add_bullet_list_item("Multi-region deployment for latency reduction", 0);
878 doc.add_bullet_list_item(
879 "Self-service model deployment portal for data science teams",
880 0,
881 );
882
883 // ── Budget ──
884 doc.add_paragraph("Budget Breakdown").style("Heading1");
885 {
886 let mut tbl = doc
887 .add_table(6, 3)
888 .borders(BorderStyle::Single, 4, "1B2A4A")
889 .width_pct(100.0);
890 tbl.cell(0, 0).unwrap().set_text("Category");
891 tbl.cell(0, 0).unwrap().shading("1B2A4A");
892 tbl.cell(0, 1).unwrap().set_text("Cost");
893 tbl.cell(0, 1).unwrap().shading("1B2A4A");
894 tbl.cell(0, 2).unwrap().set_text("Notes");
895 tbl.cell(0, 2).unwrap().shading("1B2A4A");
896 let items = [
897 (
898 "Infrastructure",
899 "$450,000",
900 "Cloud compute + GPU instances",
901 ),
902 ("Engineering Services", "$1,600,000", "12 FTE x 18 months"),
903 (
904 "Software Licenses",
905 "$350,000",
906 "Kafka, monitoring, CI/CD tools",
907 ),
908 (
909 "Training & Enablement",
910 "$200,000",
911 "Team training, documentation",
912 ),
913 ("Contingency (10%)", "$200,000", "Risk buffer"),
914 ];
915 for (i, (cat, cost, note)) in items.iter().enumerate() {
916 tbl.cell(i + 1, 0).unwrap().set_text(cat);
917 tbl.cell(i + 1, 1).unwrap().set_text(cost);
918 tbl.cell(i + 1, 2).unwrap().set_text(note);
919 if i % 2 == 0 {
920 tbl.cell(i + 1, 0).unwrap().shading("F4F1EB");
921 tbl.cell(i + 1, 1).unwrap().shading("F4F1EB");
922 tbl.cell(i + 1, 2).unwrap().shading("F4F1EB");
923 }
924 }
925 }
926
927 // ── Closing ──
928 doc.add_paragraph("");
929 doc.add_paragraph("Next Steps").style("Heading1");
930 doc.add_numbered_list_item(
931 "Schedule technical deep-dive with GFC engineering team (Week 1)",
932 0,
933 );
934 doc.add_numbered_list_item("Finalize scope and sign SOW (Week 2-3)", 0);
935 doc.add_numbered_list_item("Kick off Phase 1 with joint planning session (Week 4)", 0);
936
937 doc.add_paragraph("");
938 {
939 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
940 p.add_run("Walter White")
941 .bold(true)
942 .size(14.0)
943 .color("1B2A4A");
944 }
945 {
946 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
947 p.add_run("CEO, Tensorbee | walter@tensorbee.com")
948 .size(10.0)
949 .color("888888");
950 }
951
952 doc
953}Auto Trait Implementations§
impl Freeze for StyleBuilder
impl RefUnwindSafe for StyleBuilder
impl Send for StyleBuilder
impl Sync for StyleBuilder
impl Unpin for StyleBuilder
impl UnsafeUnpin for StyleBuilder
impl UnwindSafe for StyleBuilder
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
Mutably borrows from an owned value. Read more
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
Convert this type into
T, using the provided data to resolve any offsets.