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