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

Auto Trait Implementations§

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<T> Same for T

Source§

type Output = T

Should always be Self
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.