Skip to main content

StyleBuilder

Struct StyleBuilder 

Source
pub struct StyleBuilder { /* private fields */ }
Expand description

Builder for creating a new paragraph style.

Implementations§

Source§

impl StyleBuilder

Source

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}
Source

pub fn character(style_id: &str, name: &str) -> Self

Create a new character style builder.

Source

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}
Source

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

Set the next style (applied to the following paragraph after pressing Enter).

Source

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}
Source

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§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> Finish for T

Source§

fn finish(self)

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

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

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

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

Source§

fn to_owned_table(&self) -> U

Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.